home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 June / EnigmA AMIGA RUN 08 (1996)(G.R. Edizioni)(IT)[!][issue 1996-06][EARSAN CD VII].iso / earcd / comm2 / mmstrtrk.lha / MM / Rexx / MM_StarTrack.rexx < prev    next >
OS/2 REXX Batch file  |  1996-04-28  |  85KB  |  3,546 lines

  1. /*
  2.  
  3.                     $VER: MM_StarTrack.rexx 0.90  (28.04.96)
  4.  
  5.                            (C) 1994-96 Robert Hofmann
  6.  
  7. */
  8.  
  9. parse arg opts
  10.  
  11. options cache
  12. options failat 99
  13. options results
  14.  
  15. signal on break_c
  16. signal on break_d
  17. signal on break_e
  18. signal on break_f
  19. signal on halt
  20. signal on ioerr
  21. signal on syntax
  22.  
  23. address 'MAILMANAGER'
  24.  
  25.  
  26. Main:
  27.  
  28.     call Init
  29.     call Header
  30.     call Parse_Args(opts)
  31.     call Get_SystemDatas()
  32.     call Read_Cfg()
  33.     call pragma('p', system.taskpri)
  34.     call Wait_AreasWindow
  35.     call Check_NodeList
  36.  
  37.     do n=0 to system.matrix.count-1
  38.         call Process_Msgs(system.matrix.area.n, system.matrix.addr.n)
  39.     end
  40.  
  41.     if system.cse.count>1    then call Bounce_SplitEncoded
  42.  
  43.     call Clean_Up
  44.  
  45. call Quit(0, 'All done.')
  46. exit
  47.  
  48.  
  49. Add_CheckSplitEncoded: procedure Expose msg. system.
  50.  
  51.     parse arg areatag, nr, size
  52.  
  53.     last    = system.cse.count-1
  54.     s            = 'a'x
  55.     check    = upper(msg.data.from || s || msg.data.fromaddr || s || msg.data.to || s || msg.data.toaddr)
  56.  
  57.     if last>-1    then
  58.         if system.cse.data.last~=check    then
  59.             do
  60.                 system.cse.area.count    = 0
  61.                 system.cse.msg.count    = 0
  62.                 system.cse.data.count    = 0
  63.             end
  64.  
  65.     MM_AddToStem 'system.cse.area'    'areatag'
  66.     MM_AddToStem 'system.cse.msg'        'nr'
  67.     MM_AddToStem 'system.cse.data'    'check'
  68.  
  69.     system.cse.count    = system.cse.area.count
  70.  
  71. return
  72.  
  73.  
  74. Add_Clean: procedure Expose system.
  75.  
  76.     arg area
  77.  
  78.     if find(system.clean, area)>0 then return
  79.  
  80.     system.clean    = system.clean area
  81.  
  82.     MM_AddToStem 'system.clean' 'area'
  83.  
  84. return
  85.  
  86.  
  87. Add_Clip: procedure
  88.  
  89.     arg area .
  90.  
  91.     tmp    = getclip('MM_EXPORT')
  92.  
  93.     if find(tmp, area)=0 then call setclip('MM_EXPORT', tmp area)
  94.  
  95. return
  96.  
  97.  
  98. Add_Found: procedure Expose found. system.
  99.  
  100.     parse arg line
  101.  
  102.     if index(found.check, line'a'x)>0 then return 0
  103.  
  104.     MM_AddToStem 'found' 'line'
  105.  
  106.     found.check    = found.check line'a'x
  107.  
  108. return 0
  109.  
  110.  
  111. Add_Kludge: procedure Expose msg. system. write.
  112.  
  113.     parse arg type, kludge
  114.  
  115.     stem.0    = 'msg.data.'
  116.     stem.1    = 'write.'
  117.     tmp            = '1'x || kludge
  118.  
  119.     MM_AddToStem stem.type'head' 'tmp'
  120.  
  121. return
  122.  
  123.  
  124. Add_Log: procedure Expose msg. system.
  125.  
  126.     parse arg pfx.1, text.1, pfx.2, text.2
  127.  
  128.     if text.1='' & text.2~='' then
  129.         do
  130.             pfx.1        = pfx.2
  131.             pfx.2     = ''
  132.             text.1    = text.2
  133.             text.2    = ''
  134.         end
  135.  
  136.     if text.1~='' then
  137.         do
  138.             do n=1 to 2
  139.                 if pfx.n~='' then    pfx.n    = overlay(pfx.n, system.log.prefix)
  140.                 else                            pfx.n    = system.log.etypfx
  141.             end
  142.  
  143.             text    = pfx.1 || text.1
  144.  
  145.             if text.2~='' then text = left(text, (system.log.linelen%2)-1) pfx.2 || text.2
  146.         end
  147.  
  148.     MM_AddToStem 'msg.log' 'text'
  149. return
  150.  
  151.  
  152. Add_Stat: procedure Expose statistic.
  153.  
  154.     parse arg text, num .
  155.  
  156.     if num~='' then text = overlay('   'text' ', '   ......................:') right(num, 5)
  157.  
  158.     MM_AddToStem 'statistic' 'text'
  159. return
  160.  
  161.  
  162. Add_Status: procedure Expose msg.
  163.  
  164.     parse arg line
  165.  
  166.     MM_AddToStem 'msg.status' 'line'
  167. return
  168.  
  169.  
  170. Add_Via: procedure Expose msg. system.
  171.  
  172.     arg stem
  173.     tmp    = '1'x'Via' system.addr delstr(date(), 8, 2)'  'time() '('system.prg.fid')'
  174.     MM_AddToStem stem 'tmp'
  175. return
  176.  
  177.  
  178. Add_Write: procedure Expose write.
  179.  
  180.     parse arg text
  181.     MM_AddToStem 'write.text' 'text'
  182. return
  183.  
  184.  
  185. Adjust_Addresses: procedure Expose domain. msg. system.
  186.  
  187.     from_addr    = Check_Addr(msg.data.fromaddr)
  188.     to_addr        = Check_Addr(msg.data.toaddr  )
  189.     adj                = 0
  190.  
  191.     if from_addr~=''    then
  192.         if upper(msg.data.fromaddr)~=upper(from_addr) then
  193.             do
  194.                 msg.data.fromaddr    = from_addr
  195.                 call Set_MsgChanged
  196.             end
  197.  
  198.     if to_addr~=''        then
  199.         if upper(msg.data.toaddr)~=upper(to_addr)            then
  200.             do
  201.                 msg.data.toaddr        = to_addr
  202.                 call Set_MsgChanged
  203.             end
  204.  
  205. return
  206.  
  207.  
  208. Adjust_Kludges: procedure Expose msg. system.
  209.  
  210.     new.    = 0
  211.  
  212.     do n=0 to msg.data.head.count-1
  213.         parse value upper(msg.data.head.n) with . 2 kludge .
  214.  
  215.         if find(system.rmkludges, kludge)=0 then MM_AddToStem 'new' 'msg.data.head.'n
  216.     end
  217.  
  218.     if msg.data.head.count~=new.count then
  219.         do
  220.             call Drop_SubStem('msg.data.head')
  221.  
  222.             do n=0 to new.count-1
  223.                 msg.data.head.n    = new.n
  224.             end
  225.  
  226.             msg.data.head.count    = new.count
  227.         end
  228.  
  229. return
  230.  
  231.  
  232. Analyse_Kludges: procedure Expose msg. domain. system.
  233.  
  234.     msg.data.kludge.reply    = Get_Kludge('MSGID:',    '1'x'REPLY:')
  235.     msg.data.replyaddr        = Get_Kludge('REPLYADDR', 'To:')
  236.     tmp                                        = Get_Kludge('REPLYTO')
  237.  
  238.     if tmp~='' then
  239.         do
  240.             parse var tmp check name
  241.             address = Check_Addr(check)
  242.  
  243.             if address='' then break
  244.  
  245.             msg.data.fromaddr = address
  246.  
  247.             name = strip(name)
  248.             if name~='' then msg.data.from = name
  249.         end
  250.  
  251.     msg.data.kludges = 1
  252.  
  253. return
  254.  
  255.  
  256. Both_Unknown: procedure Expose msg. system.
  257.  
  258.     parse arg area, nr
  259.  
  260.     call Log('   UNKKNOWN SOURCE & DESTINATION!!!')
  261.     call Move_Msg(area, nr, system.badarea, 'Unknown source & destination')
  262.     call Add_Status('SOURCE and DESTINATION-address unknown or incorrect, msg stopped!')
  263.     call Count_Stat('UNKNDST')
  264.     call Count_Stat('UNKNSRC')
  265. return
  266.  
  267.  
  268. Bounce_Mail: procedure Expose domain. msg. system.
  269.  
  270.     parse arg area, nr
  271.  
  272.     call Log('   UNKKNOWN DESTINATION!!!  From' msg.data.from 'to' msg.data.to)
  273.  
  274.     call Forward_Msg('Bounced', msg.data.fromlang, area, msg.data.from, msg.data.fromaddr,,
  275.                                         Get_Text(msg.data.fromlang, 'SUBJ', 'BOUNCE_UNKNDST'),, 1)
  276.  
  277.     call Move_Msg(area, nr, system.badarea, 'Unknown destination')
  278.  
  279.     call Add_Status('Unknown DESTINATION-address detected:' msg.data.toaddr', *** BOUNCED ***')
  280.     call Count_Stat('UNKNDST')
  281. return
  282.  
  283.  
  284. Bounce_SplitEncoded: procedure Expose domain. system.
  285.  
  286.     do n=0 to system.cse.count-1
  287.         dmn        = Read_Msg(system.cse.area.n, system.cse.msg.n)
  288.         last    = n-1
  289.  
  290.         call Add_Clean(system.cse.area.n)
  291.  
  292.         if system.cse.data.last~=system.cse.data.n    then
  293.             do
  294.                 call Log(' Splitted encoded mails detected:' system.cse.area.n', starting at msg #'system.cse.msg.n)
  295.                 call Bounce_Twit('Split-Encoded', system.cse.area.n, system.cse.msg.n, domain.dmn.encoded.mode)
  296.  
  297.                 move    = find(domain.dmn.encoded.mode, 'MOVE')>0
  298.  
  299.                 call Count_Stat('ENCODED')
  300.             end
  301.         else
  302.             if move then call Move_Msg(system.cse.area.n, system.cse.msg.n, system.badarea, 'Splitted Encoded Mail')
  303.             else
  304.                 do
  305.                     MM_DeleteMsg system.cse.area.n system.cse.msg.n
  306.                     call Log('   Msg #' system.cse.msg.n 'deleted!')
  307.                 end
  308.  
  309.         call Log_Msg(dmn)
  310.     end
  311.  
  312. return
  313.  
  314.  
  315. Bounce_Twit: procedure Expose domain. msg. system.
  316.  
  317.     parse arg kind, area, nr, mode
  318.  
  319.     ukind = upper(kind)
  320.  
  321.     call Log('   'ukind 'MAIL!!!  From' msg.data.from 'to' msg.data.to)
  322.  
  323.     if find(mode, 'BOUNCE')>0 then
  324.         do
  325.             call Forward_Msg(kind, msg.data.fromlang, area, msg.data.from, msg.data.fromaddr,,
  326.                                              Get_Text(msg.data.fromlang, 'SUBJ', 'BOUNCE_'compress(ukind, '-')),, 1)
  327.  
  328.             info    = '*** NETMAIL BOUNCED ***'
  329.             ret        = 1
  330.         end
  331.     else
  332.         do
  333.             info    = ''
  334.             ret        = 0
  335.         end
  336.  
  337.     if find(mode, 'MOVE')>0 then call Move_Msg(area, nr, system.badarea, kind)
  338.     else
  339.         do
  340.             MM_DeleteMsg area nr
  341.             call Log('   Msg deleted!')
  342.         end
  343.  
  344.     call Add_Status(ukind 'MAIL detected!!!' info)
  345.     call Count_Stat(ukind)
  346. return ret
  347.  
  348.  
  349. break_c:; break_d:; break_e:; break_f:; halt:
  350.  
  351.     signal off break_c
  352.     signal off break_d
  353.     signal off break_e
  354.     signal off break_f
  355.     signal off halt
  356.  
  357.     return_code        =    5
  358.     error_line    = 0
  359.     error_msg            = 'Execution halted!!!'
  360.     rc                        = 0
  361. signal Exit
  362.  
  363.  
  364. Check_Addr: procedure Expose domain. system.
  365.  
  366.     parse arg check, mode
  367.  
  368.     parse value strip(check, 'b', '. ') with zone ':' net '/' node '@' dmn '.' .
  369.     parse var node node '.' point .
  370.  
  371.     if point='' then point = 0
  372.  
  373.     if ~datatype(zone, 'N') | ~datatype(net, 'N') | ~datatype(node, 'N') | ~datatype(point, 'N'),
  374.      | zone=0 | net=0 then return ''
  375.  
  376.     if mode~='NOADJ' then
  377.         do
  378.             tmp = system.domain
  379.             if domain='' | domain.tmp.adjust | mode='ADJUST' then dmn = Get_Domain(check)
  380.         end
  381.     else
  382.         if dmn='' then return ''
  383.  
  384. return zone':'net'/'node'.'point'@'dmn
  385.  
  386.  
  387. Check_Address: procedure Expose domain. msg. system.
  388.  
  389.     arg mode, addr . 1 zone . ':' .
  390.  
  391.     node    = mode'NODE'
  392.     real    = mode'ADDR'
  393.     tmp        = mode'DOMAIN'
  394.     dmn        = msg.data.tmp
  395.  
  396.     if find(system.addresses, msg.data.node)>0 then
  397.         return find(system.addresses system.nodes, upper(msg.data.real))=0
  398.  
  399.     if find(system.nodes, msg.data.node)>0 then return 0
  400.  
  401.     if domain.dmn.zones~='' & domain.dmn.bounce.wrongaddr then
  402.         if find(domain.dmn.zones, zone)=0    | dmn=system.baddomain then return 1
  403.  
  404.     if ~domain.dmn.bounce.unkndst then return 0
  405.  
  406.     MM_GetNodelistNode msg.data.node 'tmp'; ret = rc
  407.  
  408.     if ret=0 then return 0
  409.     if ret=5 then
  410.         do
  411.             call Log('*** INFO: Unable to access nodelist(s)!!!')
  412.             return 0
  413.         end
  414.  
  415. return 1
  416.  
  417.  
  418. Check_AllowEncoded: procedure Expose domain. msg. system.
  419.  
  420.     parse arg dmn
  421.  
  422.     if ~domain.dmn.fkt.allowenc    then return 0
  423.  
  424.     enc.0 = 'FROM'
  425.     enc.1    = 'TO'
  426.  
  427.     do n=0 to 1
  428.         typ        = enc.n
  429.         field    = typ'ADDR'
  430.  
  431.         do m=0 to domain.dmn.encoded.typ.count-1
  432.             if Check_Pattern(domain.dmn.encoded.typ.m, msg.data.field) then return 1
  433.         end
  434.     end
  435.  
  436. return 0
  437.  
  438.  
  439. Check_CrossNet: procedure Expose msg. domain.
  440.  
  441.     parse arg dmn
  442.  
  443.     if msg.data.fromdomain~=msg.data.todomain then return 1+domain.dmn.bounce.crossnet
  444.  
  445. return 0
  446.  
  447.  
  448. Check_Empty: procedure Expose msg. system.
  449.  
  450.     arg address
  451.  
  452.     if ~msg.system                                                                    then return 0
  453.     if ~msg.sysop & find(msg.data.flags, 'FATT')>0    then return 0
  454.  
  455.     check = 0
  456.     tear    = 0
  457.  
  458.     do n=0 to msg.data.text.count-1 while ~(check | tear)
  459.         parse var msg.data.text.n first .
  460.  
  461.         tear    = first='---'
  462.         check = strip(msg.data.text.n)~='' & ~tear
  463.     end
  464.  
  465. return check=0
  466.  
  467.  
  468. Check_Encoded: procedure Expose domain. msg. system.
  469.  
  470.     arg area, nr, dmn, msgsize
  471.  
  472.     if msg.system | msg.link            then return ''
  473.     if domain.dmn.encoded.size=0    then return ''
  474.  
  475.     check_split    = find(domain.dmn.encoded.mode, 'SPLIT')>0
  476.  
  477.     if msgsize<domain.dmn.encoded.size & ~check_split    then return ''
  478.  
  479.     enc            = 0
  480.     enc_len    = 0
  481.  
  482.     do n=msg.data.text.count-1 to 0 by -1
  483.         len        = length(msg.data.text.n)
  484.         check    = len-(len-length(compress(msg.data.text.n)))-lastpos(' ', msg.data.text.n)
  485.  
  486.         if last_len~=check then cnt = 0
  487.  
  488.         last_len = len
  489.  
  490.         if len<50    then iterate
  491.         if cnt>0    then enc_len    = enc_len + last_len
  492.  
  493.         cnt    = cnt+1
  494.  
  495.         if cnt<max(domain.dmn.encoded.size%len, 10) then iterate
  496.  
  497.         enc = 1
  498.         leave
  499.     end
  500.  
  501.     if enc | (~enc & check_split)            then    allowed    = ~enc | Check_AllowEncoded(dmn)
  502.     else                                                                        allowed    = 1
  503.  
  504.     if ~enc & ~allowed & check_split    then    call Add_CheckSplitEncoded(area, nr, enc_len)
  505.  
  506.     if allowed                                                then    ret         = ''
  507.     else                                                                        ret            = '*' domain.dmn.encoded.mode
  508.  
  509. return ret
  510.  
  511.  
  512. Check_FATT: procedure Expose msg. domain. rc. system.
  513.  
  514.     arg dmn
  515.  
  516.     if ~domain.dmn.fatt                                then return 0
  517.     if find(msg.data.flags, 'FATT')=0 then return 0
  518.     if msg.system | msg.link                    then return msg.sysop=0
  519.  
  520. return 2
  521.  
  522.  
  523. Check_Function: procedure Expose domain. msg. rc. system.
  524.  
  525.     arg function
  526.  
  527.     ret    = Check_Matching_Pattern(function,    msg.data.from,    msg.data.fromaddr,    msg.data.subj),
  528.                 Check_Matching_Pattern(function,    msg.data.to,        msg.data.toaddr,        msg.data.subj)
  529.  
  530. return Set_RC(function, strip(ret))
  531.  
  532.  
  533. Check_Hex: procedure
  534.  
  535.     arg hex hash ., len
  536.  
  537. return hex~='' & datatype(hex, 'X') & length(hex)=len & d2x(hash(hex))=hash
  538.  
  539.  
  540. Check_Kill: procedure Expose msg. system.
  541.  
  542.     if ~system.fkt.kill | ~msg.sysop then return ''
  543.  
  544. return Check_Matching_Pattern('KILL', msg.data.from, msg.data.fromaddr, msg.data.subj)
  545.  
  546.  
  547. Check_Loop: procedure Expose domain. msg. system.
  548.  
  549.     arg check, area, nr
  550.  
  551.     if ~check then return 0
  552.  
  553.     check.        = 0
  554.     last_addr    = ''
  555.  
  556.     do n=0 to msg.data.foot.count-1
  557.         if compress(msg.data.foot.n, '010D'x)='' then iterate
  558.  
  559.         addr = upper(Get_Addr_From_Via(msg.data.foot.n))
  560.  
  561.         select
  562.             when addr='???'    then nop
  563.             when check.addr    & last_addr~=addr & find(system.addresses, addr)>0 then return 1
  564.             otherwise
  565.                 do
  566.                     check.addr    = 1
  567.                     last_addr        = addr
  568.                 end
  569.         end
  570.     end
  571.  
  572. return 0
  573.  
  574.  
  575. Check_Matching_Pattern: procedure Expose msg. system.
  576.  
  577.     arg type, stem.name.0, stem.addr.0, stem.subj.0
  578.  
  579.     if system.fkt.np.type    then
  580.         do n=0 to system.check.count-1
  581.             field                            = system.check.n
  582.             stem.field.count    = 1
  583.  
  584.             do m=0 to system.type.field.ptrn.count-1
  585.                 result.    = 0
  586.                 MM_SearchInStem 'stem.'field 'result' system.type.field.ptrn.m 'STR'
  587.  
  588.                 if result.count>0 then return system.type.field.mode.m
  589.             end
  590.         end
  591.  
  592.     if system.fkt.fp.type    then
  593.         do n=0 to system.ptrn.type.from.count-1
  594.             cnt                    = 0
  595.             stem.count    = 1
  596.  
  597.             do m=0 to system.full_check.count-1
  598.                 result.            = 0
  599.                 field                = system.full_check.m
  600.                 stem.0            = msg.data.field
  601.  
  602.                 MM_SearchInStem 'stem' 'result' '"'system.ptrn.type.field.n'"' 'NUM'
  603.  
  604.                 cnt    = cnt+(result.count>0)
  605.             end
  606.  
  607.             if cnt=system.full_check.count then return system.ptrn.type.mode.n
  608.         end
  609.  
  610. return ''
  611.  
  612.  
  613. Check_NodeList: procedure Expose system.
  614.  
  615.     check    = Get_Node(system.addresses.0)
  616.     tmp        = 'Unable to access nodelist(s)!!!'
  617.  
  618.     do n=0 to 5
  619.         MM_GetNodelistNode check 'tmp'
  620.         err = RC>0
  621.  
  622.         if ~err then leave
  623.  
  624.         call Log('*** WARNING:' tmp '('check 'not found)')
  625.         call Log('             'n'> Waiting 30 seconds...')
  626.         call delay(30*50)
  627.     end
  628.  
  629.     if err then
  630.         do
  631.             if system.cplnl~=0 then
  632.                 do
  633.                     call Log(' ==>> Compiling nodelists!')
  634.                     call Command(system.cplnl)
  635.  
  636.                     MM_GetNodelistNode check 'tmp'
  637.                     err = RC>0
  638.                 end
  639.  
  640.             if err then call Quit(24, tmp)
  641.         end
  642.  
  643. return
  644.  
  645.  
  646. Check_Pattern: procedure Expose result
  647.  
  648.     arg pattern, string.0
  649.  
  650.     string.count    = 1
  651.     result.                = 0
  652.  
  653.     MM_SearchInStem 'string' 'result' pattern 'STR'
  654.  
  655.     result                = result.0
  656.  
  657. return result.count>0
  658.  
  659.  
  660. Check_Processed: procedure Expose domain. system.
  661.  
  662.     /* If you disable this routine, you are not allowed to use MM_StarTrack anymore! */
  663.  
  664.     system.stat.ucnt    = system.stat.ucnt+1
  665.     last_check                = system.stat.uchk
  666.     system.stat.uchk    = 12+left(date('u'), 2)
  667.     tmp               = date('i')-system.stat.uday
  668.  
  669.     if system.stat.uchk=last_check | tmp<30 | datatype(substr(system.prg.state, 2), 'N') then return
  670.  
  671.     call Msg_Head('4E6F74696669636174696F6E2061626F757420746865207573616765206F66'x system.prg.fid)
  672.     call Get_SystemInfo()
  673.     call Notify_Author('5573616765206F66'x system.prg.name)
  674.  
  675. return
  676.  
  677.  
  678. Check_RRR: procedure Expose domain. msg. system.
  679.  
  680.     arg dmn
  681.  
  682.     if find(msg.data.flags, 'RRR')=0 then return 0
  683.  
  684.     if domain.tmp.rrr.system then
  685.         if msg.sysop then return 1
  686.  
  687.     if domain.tmp.rrr.points then
  688.         if find(system.addresses, upper(msg.data.tonode))>0    then return 1
  689.  
  690. return 0
  691.  
  692.  
  693. Check_Twit: procedure Expose domain. msg. rc. system.
  694.  
  695.     if msg.sysop then return ''
  696.  
  697. return Check_Function('TWIT')
  698.  
  699.  
  700. Clean_Up:
  701.  
  702.     if ~system.nostats & system.stats then call Write_Stats
  703.  
  704.     if system.export.count=0 & system.clean.count>0 then
  705.         do n=0 to system.clean.count-1
  706.             MM_CleanArea system.clean.n
  707.         end
  708.  
  709.     do n=0 to system.export.count-1
  710.         MM_Export            system.export.n
  711.         MM_Delete            system.export.n
  712.         MM_CleanArea  system.export.n
  713.     end
  714.  
  715.     call delete(system.tmpfile)
  716. return
  717.  
  718.  
  719. Command: procedure Expose system.
  720.  
  721.     parse arg cmd
  722.  
  723.     address command cmd
  724.  
  725.     if rc>0 then call Log('*** ERROR: Command "'cmd'" returned' rc'.')
  726. return rc
  727.  
  728.  
  729. Count_Stat: procedure Expose system.
  730.  
  731.     arg mode ., cnt
  732.  
  733.     if cnt=''                                    then cnt                            = 1
  734.     if system.stat.mode>65534 then system.stat.mode = 0
  735.  
  736.     system.stat.mode = system.stat.mode+cnt
  737. return
  738.  
  739.  
  740. Cross_Net: procedure Expose domain. msg. system.
  741.  
  742.     parse arg area, nr, mode, src
  743.  
  744.     call Log('   CROSSNET!!!')
  745.  
  746.     if mode<2 then
  747.         do
  748.             call Forward_Msg('CrossNet_ToDst', msg.data.tolang, area, msg.data.to, msg.data.toaddr,,
  749.                                                 Get_Text(msg.data.tolang, 'SUBJ', 'CROSSNET_TODST'),, 0)
  750.  
  751.             info    = 'Info added.'
  752.         end
  753.     else info = '*** BOUNCED ***'
  754.  
  755.     if ~src then
  756.         do
  757.             if mode<2 then tmp = 'CROSSNET_TOSRC'
  758.             else                     tmp = 'BOUNCE_CROSSNET'
  759.  
  760.             call Forward_Msg('CrossNet_ToSrc', msg.data.fromlang, area, msg.data.from, msg.data.fromaddr,,
  761.                                                 Get_Text(msg.data.fromlang, 'SUBJ', tmp),, 1)
  762.  
  763.             resend = 'Msg resend.'
  764.         end
  765.     else resend = ''
  766.  
  767.     call Move_Msg(area, nr, system.badarea, 'Cross-Net')
  768.  
  769.     call Add_Status(strip('Crossnet-Netmail detected!' info resend))
  770.     call Count_Stat('CROSSNET')
  771. return
  772.  
  773.  
  774. Cut_Text: procedure Expose msg.
  775.  
  776.     parse arg num
  777.  
  778.     tmp                                    = num+1
  779.     msg.data.text.num        = '[...]'
  780.     msg.data.text.tmp        = ''
  781.     msg.data.text.count    = num+2
  782.  
  783. return
  784.  
  785.  
  786. d2h: procedure
  787.  
  788.     arg num
  789. return right(d2x(num), 4, '0')
  790.  
  791.  
  792. Debug: procedure Expose system.
  793.  
  794.     if ~system.debug then return
  795.  
  796.     parse arg pfx, line
  797.  
  798.     if line~='' then line = 'DEBUG -' pfx':' line
  799.  
  800.     call Log(line, '*')
  801.  
  802. return
  803.  
  804.  
  805. Del_Flag: procedure Expose msg.
  806.  
  807.     arg flag
  808.  
  809.     p    = find(msg.data.flags, flag)
  810.  
  811.     if p>0    then delword(msg.data.flags, p)
  812.  
  813. return
  814.  
  815.  
  816. Drop_SubStem:
  817.  
  818.     arg stem
  819.  
  820.     interpret "do nn=0 to" stem".count-1; drop" stem".nn; end;" stem".count = 0"
  821. return
  822.  
  823.  
  824. Execute_Cmd: procedure Expose msg. rc. system.
  825.  
  826.     parse arg area, nr
  827.  
  828.     call Get_FunctionDatas('EXECUTE')
  829.  
  830.     do n=0 to found.count-1
  831.         cmd    = translate(found.n, '2227'x, 'ø¶')
  832.         cmd    = Replace_Embedded(cmd)
  833.         cmd    = Replace(cmd, area, '%a')
  834.         cmd    = Replace(cmd, nr,   '%n')
  835.         p        = pos('%T', cmd)
  836.  
  837.         if p>0 then
  838.             do
  839.                 tmp_l =     left(cmd, p-1)
  840.                 tmp_r    = substr(cmd, p+2)
  841.  
  842.                 parse var tmp_r msgfile tmp_r
  843.  
  844.                 cmd    = strip(strip(tmp_l) strip(tmp_r))
  845.  
  846.                 MM_WriteStem msgfile 'msg.data.head'
  847.                 MM_WriteStem msgfile 'msg.data.text' 'APPEND'
  848.                 MM_WriteStem msgfile 'msg.data.foot' 'APPEND'
  849.             end
  850.  
  851.         call Log('   Executing "'cmd'".',, 3)
  852.  
  853.         ret = Command(cmd)
  854.  
  855.         select
  856.             when ret=0    then    nop
  857.             when ret=1    then    call Set_RC('KILL',            rc.kill 'BOUNCE MOVE')
  858.             when ret=2    then    call Set_RC('TWIT',            rc.twit 'BOUNCE MOVE')
  859.             otherwise                    call Set_RC('EXCLUDE',    '*')
  860.         end
  861.     end
  862.  
  863. return
  864.  
  865.  
  866. Exit:
  867.  
  868.     if return_code>29 then call Report_Error(return_code, error, error_msg)
  869.  
  870.     select
  871.         when return_code>=40 then error = 'INTERNAL-ERROR:'
  872.         when return_code>=30 then error = 'IO-ERROR:'
  873.         when return_code>=20 then error = 'ERROR:'
  874.         when return_code>=10 then error = 'CFG-ERROR:'
  875.         when return_code>=5  then error = 'INFO:'
  876.         otherwise                                    error = ''
  877.     end
  878.  
  879.     call Log(,, 3)
  880.     call Log('***' strip(error error_msg) '***', '+')
  881.     call Log(, '\', 3)
  882.  
  883.     call setclip('MM_LogPre', system.mm.logpre)
  884.  
  885. exit return_code
  886.  
  887.  
  888. File: procedure
  889.  
  890.     parse arg file
  891.  
  892. return substr(file, max(lastpos(':', file), lastpos('/', file))+1)
  893.  
  894.  
  895. Format_Log: procedure Expose msg. system.
  896.  
  897.     parse arg pfx, text
  898.  
  899.     text    = strip(text)
  900.  
  901.     if length(word(text, 1))>system.log.txtlen then
  902.         do
  903.             tmp        = substr(text, system.log.txtlen+1)
  904.             text    = strip(left(text, system.log.txtlen))
  905.             call         Add_Log(pfx, text)
  906.             call         Format_Log('', tmp)
  907.             return
  908.         end
  909.  
  910.     tlen = length(text)
  911.  
  912.     if tlen>system.log.txtlen then
  913.         do
  914.             lsp        = lastpos(' ', text, system.log.txtlen)
  915.             tmp        = substr(text, lsp)
  916.             text    = strip(left(text, lsp-1))
  917.             call        Add_Log(pfx,  text)
  918.             call        Format_Log('', tmp)
  919.         end
  920.     else call        Add_Log(pfx,  text)
  921.  
  922. return
  923.  
  924.  
  925. Forward: procedure Expose domain. msg. rc. system.
  926.  
  927.     parse arg matrix, nr
  928.  
  929.     call Get_FunctionDatas('FORWARD')
  930.  
  931.     do n=0 to found.count-1
  932.         parse value translate(found.n, '2227'x, 'ø¶') with to.area '¡' to.name '¡' to.addr '¡' to.subj '¡' to.flags '¡' delmsg
  933.  
  934.         if to.area='%ma'    then to.area    = matrix
  935.  
  936.         call Log('   Forwarding mail from' msg.data.from 'to' to.area',' to.name || strip(',' to.addr, 't', ', ')'...')
  937.  
  938.         MM_GetAreaInfo to.area 'tmp'
  939.         if tmp.type='MAIL' then    lang    = msg.data.tolang
  940.         else                                        lang    = Get_Language(tmp.addr)
  941.  
  942.         call Forward_Msg('Forward', lang, to.area, strip(to.name), to.addr,,
  943.                                             Replace_Embedded(strip(to.subj)), to.flags, 0)
  944.  
  945.         call Add_Clip(to.area)
  946.  
  947.         if delmsg    then
  948.             do
  949.                 MM_DeleteMsg matrix nr
  950.                 call Log('   -> Original msg deleted!')
  951.             end
  952.     end
  953.  
  954. return delmsg
  955.  
  956.  
  957. Forward_Msg: procedure Expose domain. msg. system. write.
  958.  
  959.     parse arg file, language, area, dst, dstaddr, subject, flags, is_reply
  960.  
  961.     if file~='Forward' then
  962.         do
  963.             tmp = Get_Node(dstaddr)
  964.  
  965.             MM_GetNodelistNode tmp 'tmp'
  966.             if RC~=0 then
  967.                 do
  968.                     call Log('   *** Unknown destination-address' dstaddr', unable to forward mail!')
  969.                     return
  970.                 end
  971.  
  972.             dst = Get_Sysop(dst, dstaddr)
  973.  
  974.             call Log('   Sending mail to' dst',' dstaddr,, 3)
  975.         end
  976.  
  977.     textfile    = Read_File(file, language)
  978.  
  979.     write. = 0
  980.  
  981.     if is_reply then
  982.         do
  983.             if ~msg.data.kludges then call Analyse_Kludges
  984.             if msg.data.replyaddr~='' then MM_AddToStem 'write.addtxt' 'msg.data.replyaddr'
  985.  
  986.             MM_AddToStem 'write.head' 'msg.data.kludge.reply'
  987.         end
  988.  
  989.     do n=0 to txt.count-1
  990.         tmp    = Replace_Embedded(txt.n)
  991.  
  992.         if index(tmp, '%T')=0 then call Add_Write(tmp)
  993.         else
  994.             do
  995.                 parse var tmp . '%T' lines .
  996.  
  997.                 if lines='' | lines=msg.data.text.count then lines = msg.data.text.count
  998.                 else
  999.                     do
  1000.                         if ~datatype(lines, 'N') then
  1001.                             do
  1002.                                 lines = 20
  1003.                                 call Log('*** WARNING: Numeric value expected after %T in' textfile 'at line' n'!!!')
  1004.                                 call Log('             -> Using default (='lines') instead.',, 4)
  1005.                             end
  1006.  
  1007.                         call Cut_Text(lines)
  1008.                     end
  1009.  
  1010.                 do m=0 to msg.data.head.count-1
  1011.                     tmp = strip(translate(msg.data.head.m, '@', '010D'x))
  1012.                     if tmp~='' then call Add_Write(tmp)
  1013.                 end
  1014.  
  1015.                 if msg.data.head.count>0 then call Add_Write()
  1016.  
  1017.                 do m=0 to msg.data.text.count-1
  1018.                     call Add_Write(msg.data.text.m)
  1019.                 end
  1020.  
  1021.                 do m=0 to msg.data.foot.count-1
  1022.                     tmp = strip(translate(msg.data.foot.m, '@', '010D'x))
  1023.                     if tmp~='' then call Add_Write(tmp)
  1024.                 end
  1025.             end
  1026.     end
  1027.  
  1028.     call Write_Msg('write', area, dst, dstaddr, subject, system.tmpfile, flags)
  1029. return
  1030.  
  1031.  
  1032. Get_Addr_From_Via: procedure Expose domain. system.
  1033.  
  1034.     parse arg . tmp
  1035.  
  1036.     tmp = compress(tmp, '010D'x',"')
  1037.  
  1038.     do while tmp>''
  1039.         parse var tmp check tmp
  1040.         address = Check_Addr(check)
  1041.  
  1042.         if address~='' then return address
  1043.     end
  1044.  
  1045. return '???'
  1046.  
  1047.  
  1048. Get_AddrDomain: procedure Expose system.
  1049.  
  1050.     arg .'@' domain '.' .
  1051.  
  1052. return Make_Valid(domain)
  1053.  
  1054.  
  1055. Get_Arg: procedure Expose args
  1056.  
  1057.     arg keyword, rnr
  1058.  
  1059.     p     = find(upper(args), keyword)
  1060.     ret    = 0
  1061.  
  1062.     if rnr>0 then
  1063.         if p>0 then
  1064.             do
  1065.                 ret        = subword(args, p+1, rnr)
  1066.                 args    = delword(args, p, p+rnr)
  1067.             end
  1068.         else ret            = ''
  1069.     else
  1070.         if p>0 then
  1071.             do
  1072.                 ret        = 1
  1073.                 args    = delword(args, p, 1)
  1074.             end
  1075.  
  1076.     args    = strip(args)
  1077.  
  1078. return ret
  1079.  
  1080.  
  1081. Get_Domain: procedure Expose domain. system.
  1082.  
  1083.     arg zone . ':' .
  1084.  
  1085.     l_tmp = system.domains
  1086.     u_tmp    = system.vdomains
  1087.  
  1088.     do while u_tmp~=''
  1089.         parse var l_tmp l_dmn l_tmp
  1090.         parse var u_tmp u_dmn u_tmp
  1091.  
  1092.         if find(domain.u_dmn.zones, zone)>0 then return l_dmn
  1093.     end
  1094.  
  1095.     parse var system.badaddr . '@' dmn '.' .
  1096.  
  1097. return dmn
  1098.  
  1099.  
  1100. Get_FunctionDatas: procedure Expose found. msg. system.
  1101.  
  1102.   parse arg function
  1103.  
  1104.     field.1            = 'FROM'
  1105.     field.2            = 'TO'
  1106.     found.            = 0
  1107.     found.check    = ''
  1108.     stem.subj.0    = msg.data.subj
  1109.  
  1110.     do i=1 to 2
  1111.         tmp                    = field.i
  1112.         tmp2                = tmp'ADDR'
  1113.         stem.name.0    =    msg.data.tmp
  1114.         stem.addr.0    = msg.data.tmp2
  1115.  
  1116.         do n=0 to system.check.count-1
  1117.             field                            = system.check.n
  1118.             stem.field.count    = 1
  1119.  
  1120.             do m=0 to system.function.field.ptrn.count-1
  1121.                 result.    = 0
  1122.                 MM_SearchInStem 'stem.'field 'result' system.function.field.ptrn.m 'STR'
  1123.  
  1124.                 if result.count>0 then call Add_Found(system.function.field.mode.m)
  1125.             end
  1126.         end
  1127.  
  1128.         do n=0 to system.ptrn.function.from.count-1
  1129.             cnt                    = 0
  1130.             stem.count    = 1
  1131.  
  1132.             do m=0 to system.full_check.count-1
  1133.                 result.            = 0
  1134.                 field                = system.full_check.m
  1135.                 stem.0            = msg.data.field
  1136.  
  1137.                 MM_SearchInStem 'stem' 'result' '"'system.ptrn.function.field.n'"' 'NUM'
  1138.  
  1139.                 cnt    = cnt+(result.count>0)
  1140.             end
  1141.  
  1142.             if cnt=system.full_check.count then cnt    = Add_Found(system.ptrn.function.mode.n)
  1143.         end
  1144.     end
  1145.  
  1146. return
  1147.  
  1148.  
  1149. Get_Kludge: procedure Expose msg. system.
  1150.  
  1151.     arg kludge, new_id
  1152.  
  1153.     result.     = 0
  1154.     result.0    = ''
  1155.  
  1156.     MM_SearchInStem 'msg.data.head' 'result' '"?'kludge' #?"' 'STR'
  1157.  
  1158.     parse var result.0 . ret
  1159.  
  1160.     ret = strip(ret)
  1161.  
  1162.     if ret~='' then ret = strip(new_id ret)
  1163.  
  1164. return ret
  1165.  
  1166.  
  1167. Get_Language: procedure Expose system.
  1168.  
  1169.     parse arg tmp_stem.0
  1170.     tmp_stem.count    = 1
  1171.     result.                    = 0
  1172.  
  1173.     do n=0 to system.lang.known.count-1
  1174.         tmp    = system.lang.known.n
  1175.  
  1176.         do m=0 to system.lang.ptrn.tmp.count-1
  1177.             MM_SearchInStem 'tmp_stem' 'result' system.lang.ptrn.tmp.m 'STR'
  1178.             if result.count>0 then return tmp
  1179.         end
  1180.     end
  1181.  
  1182. return ''
  1183.  
  1184.  
  1185. Get_Name: procedure
  1186.  
  1187.     parse arg address
  1188.  
  1189.     MM_GetNodelistNode address 'tmp'
  1190.  
  1191.     if rc>0 then    ret = 'Sysop'
  1192.     else                    ret = tmp.sysop
  1193.  
  1194. return ret
  1195.  
  1196.  
  1197. Get_Node: procedure
  1198.  
  1199.     parse arg left '.' . '@' right
  1200.  
  1201. return left'.0@'right
  1202.  
  1203.  
  1204. Get_Nodelists: procedure Expose system.
  1205.  
  1206.     system.nodelists = '<Nodelist not available>'
  1207.  
  1208.     if Command(system.shownl '>'system.tmpfile)>0 then return
  1209.  
  1210.     MM_ReadStem system.tmpfile 'tmp2'
  1211.     if rc>0 then return
  1212.  
  1213.     tmp.    = 0
  1214.  
  1215.     do n=1 to tmp2.count-1
  1216.         MM_AddToStem 'tmp' 'tmp2.'n
  1217.     end
  1218.  
  1219.     MM_SortStem 'tmp'
  1220.  
  1221.     system.nodelists = ''
  1222.  
  1223.     do n=0 to tmp.count-1
  1224.         system.nodelists    = system.nodelists strip(translate(tmp.n, ' ', '9'x))','
  1225.     end
  1226.  
  1227.     system.nodelists    = upper(strip(system.nodelists, 'b', ', '))
  1228.  
  1229.     call delete(system.tmpfile)
  1230. return
  1231.  
  1232.  
  1233. Get_Sysop: procedure
  1234.  
  1235.     parse arg name, address
  1236.  
  1237.     if index(address, '.0@')=0 then return name
  1238.  
  1239. return Get_Name(address)
  1240.  
  1241.  
  1242. Get_SystemDatas: procedure Expose system.
  1243.  
  1244.     MM_GetSysop        'system.sysop'
  1245.     parse var system.sysop system.sysop_first system.sysop_sur
  1246.  
  1247.     MM_GetNodes   'system.nodes'
  1248.     do n=0 to system.nodes.count-1
  1249.         system.nodes    = system.nodes system.nodes.n
  1250.     end
  1251.     upper system.nodes
  1252.  
  1253.     MM_GetAddrs                'system.addresses'
  1254.     MM_SortAddresses    'system.addresses'
  1255.  
  1256.     system.alldomains    = ''
  1257.  
  1258.     do n=0 to system.addresses.count-1
  1259.         system.addresses = system.addresses system.addresses.n
  1260.  
  1261.         tmp = Get_AddrDomain(system.addresses.n)
  1262.         if find(system.alldomains, tmp)=0 then system.alldomains    = system.alldomains tmp
  1263.     end
  1264.     upper system.addresses
  1265.  
  1266.     system.date    = translate(delstr(date(), 8, 2), '-', ' ')
  1267.     system.time = time()
  1268.  
  1269. return
  1270.  
  1271.  
  1272. Get_SystemInfo: procedure Expose system. write.
  1273.  
  1274.     /* If you disable this routine, you are not allowed to use MM_StarTrack anymore! */
  1275.  
  1276.     call Add_Write(' 'system.prg.info)
  1277.     call Add_Write()
  1278.     call Add_Write()
  1279.     call Add_Write('205379736F7020202020203A'x system.sysop)
  1280.     call Add_Write()
  1281.     call Add_Write('20416464726573736573203A'x system.addresses.count)
  1282.     call Add_Write('20446F6D61696E732020203A'x words(system.domains))
  1283.     call Add_Write('204D61696C6172656173203A'x system.matrix.count '('system.mailareas.all')')
  1284.     call Add_Write()
  1285.  
  1286.     MM_ReadStem system.prg.stats 'write.text' 'APPEND'
  1287.  
  1288.     call Add_Write()
  1289.     call Add_Write()
  1290.  
  1291.     lst = '633A6C697374'x
  1292.  
  1293.     if exists(lst) then
  1294.         do
  1295.             call Command(lst system.prg.script'#?' '>'system.tmpfile)
  1296.             MM_ReadStem system.tmpfile 'write.text' 'APPEND'
  1297.  
  1298.             call Add_Write()
  1299.             call Add_Write()
  1300.  
  1301.             call Command(lst system.prg.cfgpath '>'system.tmpfile)
  1302.             MM_ReadStem system.tmpfile 'write.text' 'APPEND'
  1303.  
  1304.             call delete(system.tmpfile)
  1305.         end
  1306.     else
  1307.         do
  1308.             parse value statef(system.prg.script)'0 0 0 0 0' with . size . . date .
  1309.             call Add_Write(' 'left(system.prg.script, 40) right(size, 8)'   'date('n', date, 'i'))
  1310.  
  1311.             parse value statef(system.prg.cfg)'0 0 0 0 0' with . size . . date .
  1312.             call Add_Write(' 'left(system.prg.cfg, 40) right(size, 8)'   'date('n', date, 'i'))
  1313.         end
  1314.  
  1315.     call Add_Write()
  1316.     call Add_Write()
  1317.     call Add_Write(' 'system.prg.info)
  1318.     call Add_Write()
  1319.     call Add_Write()
  1320.  
  1321. return
  1322.  
  1323.  
  1324. Get_Text: procedure Expose system.
  1325.  
  1326.     arg lang, typ, text
  1327.  
  1328.     if lang=''    then lang = 'DEFAULT'
  1329.  
  1330.     ret = system.txt.lang.typ.text
  1331.     if ret~=0 then return ret
  1332.  
  1333.     cfg    = '#'typ'_'text
  1334.  
  1335.     call Log('*** WARNING: Unable to get' cfg 'of language' lang'!!!')
  1336.     call Log('             -> Using default instead.',, 4)
  1337.  
  1338.     ret = system.txt.default.typ.text
  1339.     if ret~=0 then return ret
  1340.  
  1341.     if lang='DEFAULT' then file = system.prg.txtpfx'/'
  1342.     else                                     file = system.prg.txtpfx'.'lang'/'
  1343.  
  1344.     call Log('*** CFG-ERROR: Unable to get' file'Misc:' cfg'!!!')
  1345.  
  1346. return '*** ERROR DETECTED ***'
  1347.  
  1348.  
  1349. Get_Version: procedure
  1350.  
  1351.     parse arg mode
  1352.  
  1353.     parse value sourceline(3-mode) with . . ver .
  1354.     parse var ver tst 'ß' .
  1355.  
  1356.     if ~datatype(strip(tst, 'b', '/ce '), 'N') then
  1357.         if ~mode then ver = Get_Version(1)
  1358.         else exit 99
  1359.  
  1360. return ver
  1361.  
  1362.  
  1363. Header:
  1364.  
  1365.     call Log(, '/', 3)
  1366.     call Log('***' system.prg.id '***', '+')
  1367.     call Log(system.prg.shortcr,, 3)
  1368.     call Log(,,3)
  1369.  
  1370. return
  1371.  
  1372.  
  1373. Make_Valid: procedure Expose system.
  1374.  
  1375.     arg string
  1376.  
  1377. return translate(string, system.replace, system.invalid)
  1378.  
  1379.  
  1380. Init:
  1381.  
  1382.     system.                            = 0
  1383.  
  1384.     MM_GetCfgPaths                'system.mm'
  1385.     MM_Version                        'system.mm'
  1386.     MM_GetTaskPri                    'system.taskpri'
  1387.  
  1388.     call                                     pragma('p', system.taskpri)
  1389.     call                                     pragma('w', 'NULL')
  1390.  
  1391.     system.path.cfg            = 'MM:Config'
  1392.     system.prg.ver            = Get_Version(0)
  1393.     system.prg.name            = 'MM_StarTrack'
  1394.     system.prg.cfgpath    = system.path.cfg'/'system.prg.name'/'
  1395.     system.prg.txtpfx        = system.prg.cfgpath'Texts'
  1396.     system.prg.id                = system.prg.name 'v'system.prg.ver
  1397.     system.prg.pfx            = system.prg.cfgpath || system.prg.name'.'
  1398.     system.prg.cfg            = system.prg.pfx'cfg'
  1399.     system.prg.shortcr    = '28432920313939342D393620526F6265727420486F666D616E6E'x
  1400.     system.prg.cr                = system.prg.shortcr '323A323439302F313031352E30404669646F4E6574'x
  1401.     system.prg.script        = 'MM:Rexx/'system.prg.name'.rexx'
  1402.     system.prg.stats        = system.prg.pfx'Statistics'
  1403.     system.mm.logpre      = getclip('MM_LogPre')
  1404.     system.prg.logpre     = system.mm.logpre'|'
  1405.     call                                        setclip('MM_LogPre', system.prg.logpre)
  1406.     system.prg.loglevel    = 2
  1407.     system.statistics     = 'CROSSNET EMPTY ENCODED EXCLUDE FATT KILL LOOP NTD PROCESSED RMPDST',
  1408.                                                 'RMPSRC RRR SIZE TWIT UCHK UCNT UDAY UNKNDST UNKNSRC'
  1409.     system.statcnt            = words(system.statistics)*4
  1410.     system.tmpfile            = 'T:'system.prg.name'.tmp'
  1411.     system.invalid            = xrange('0'x, '@') || xrange('[', 'FF'x)
  1412.     system.replace            = copies('_', length(system.invalid))
  1413.     system.rmkludges        = '4D534749443A20444F4D41494E20494E544C20464D505420544F505420434852533A20434841525345543A'x
  1414.     system.log.linelen    = 80
  1415.     system.log.prelen        = 11
  1416.     system.log.txtlen        = system.log.linelen-system.log.prelen
  1417.     system.log.prefix   = copies('.', system.log.prelen-2)': '
  1418.     system.log.etypfx        = copies(' ', system.log.prelen)
  1419.  
  1420.     system.check.0                    = 'NAME'
  1421.     system.check.1                    = 'ADDR'
  1422.     system.check.2                = 'SUBJ'
  1423.     system.check.count            = 3
  1424.     system.full_check.0            = 'FROM'
  1425.     system.full_check.1            = 'FROMADDR'
  1426.     system.full_check.2            = 'TO'
  1427.     system.full_check.3            = 'TOADDR'
  1428.     system.full_check.4            = 'SUBJ'
  1429.     system.full_check.count    = 5
  1430.  
  1431.     call Include_Lib('rexxsupport')
  1432. return
  1433.  
  1434.  
  1435. Include_Lib: procedure
  1436.  
  1437.     parse arg lib, prio
  1438.     if right(upper(lib), 8)~='.LIBRARY' then lib=lib'.library'
  1439.     if prio='' then prio=0
  1440.  
  1441.     if ~show('l', lib) then
  1442.         if ~addlib(lib, prio, -30, 0) then
  1443.             do
  1444.                 say '*** ERROR: Could not open' lib'!!! ***'
  1445.                 exit 10
  1446.             end
  1447. return
  1448.  
  1449.  
  1450. Insert_Text: procedure Expose msg. system.
  1451.  
  1452.     parse arg file, language
  1453.  
  1454.     call Read_File(file, language)
  1455.  
  1456.     do n=0 to txt.count-1
  1457.         MM_AddToStem 'msg.data.addtxt' 'txt.'n
  1458.   end
  1459.  
  1460. return
  1461.  
  1462.  
  1463. IOerr:
  1464.  
  1465.     signal off ioerr
  1466.  
  1467.     return_code        = 30
  1468.     error_code        = rc
  1469.     error_line    = sigl
  1470.     error_msg            = 'IO-error' rc 'at line' error_line '['errortext(rc)']')
  1471.     rc                        = 0
  1472. signal Exit
  1473.  
  1474.  
  1475. Log: procedure Expose system.
  1476.  
  1477.     parse arg text, pre, level
  1478.  
  1479.     if ~datatype(level, 'N') then level = system.prg.loglevel
  1480.  
  1481.     tmp        = word('PRG MM', (pre~='')+1)
  1482.     text    = system.tmp.logpre || pre' 'text
  1483.  
  1484.     MM_WriteLog 'text' level
  1485. return
  1486.  
  1487.  
  1488. Log_Msg: procedure Expose domain. msg. system.
  1489.  
  1490.     arg dmn
  1491.  
  1492.     if domain.dmn.log.file=0 | pos('of' system.prg.name, msg.data.subj)>0 then return
  1493.  
  1494.     call Add_Log('Imported',    date()'  'time(),    'Created',        msg.data.datum msg.data.time)
  1495.     call Add_Log('From User',    msg.data.from,        'From Addr',    msg.data.fromaddr)
  1496.     call Add_Log('To   User',    msg.data.to,            'To   Addr',    msg.data.toaddr)
  1497.  
  1498.     if domain.dmn.log.size then msg_size    = msg.data.text.size 'bytes'
  1499.     else                                                msg_size    = ''
  1500.  
  1501.     if domain.dmn.log.flags then
  1502.         do
  1503.             msg_flags    = ''
  1504.             tmp                = msg.data.origflags
  1505.  
  1506.             do while tmp>''
  1507.                 parse var tmp check tmp
  1508.                 if find('PVT CRASH HOLD RRR FATT', check)=0 then iterate
  1509.  
  1510.                 msg_flags = msg_flags check
  1511.             end
  1512.  
  1513.             msg_flags    = strip(msg_flags)
  1514.  
  1515.             if msg_flags=''    then msg_flags = '<none>'
  1516.         end
  1517.     else msg_flags = ''
  1518.  
  1519.     if domain.dmn.log.size | domain.dmn.log.flags then
  1520.         call Add_Log('Size', msg_size, 'Flags', msg_flags)
  1521.  
  1522.     if domain.dmn.log.subj then    call Format_Log('Subject', msg.data.subj)
  1523.  
  1524.     if domain.dmn.log.routing then
  1525.         do
  1526.             tmp        = msg.data.foot.count-1
  1527.             do tmp=tmp to 0 by -1 while strip(msg.data.foot.tmp, 'b', '010D'x' ')=''; end
  1528.             from  = Get_Addr_From_Via(msg.data.foot.tmp)
  1529.  
  1530.             if from='???' then
  1531.                 if find(msg.data.origflags, 'CRASH')>0 then from = msg.data.fromaddr
  1532.  
  1533.             call Add_Log('Routing', 'from' from /* 'to' '???'*/ )
  1534.         end
  1535.  
  1536.     tmp.via    = 'Via'
  1537.     tmp.np    = '<no via-lines present>'
  1538.  
  1539.     if domain.dmn.log.via.all then
  1540.         if msg.data.foot.count=0 then call Add_Log(tmp.via, tmp.np)
  1541.         else
  1542.             do n=0 to msg.data.foot.count-1
  1543.                 tmp.line    = subword(strip(msg.data.foot.n, 'b', '010D'x' '), 2)
  1544.                 if tmp.line='' then iterate
  1545.  
  1546.                 call Add_Log(tmp.via, tmp.line)
  1547.                 tmp.via = ''
  1548.             end
  1549.  
  1550.     if domain.dmn.log.via.addr then
  1551.         if msg.data.foot.count=0 then call Add_Log(tmp.via, tmp.np)
  1552.         else
  1553.             do
  1554.                 tmp = ''
  1555.  
  1556.                 do n=0 to msg.data.foot.count-1
  1557.                     tmp.line    = strip(msg.data.foot.n, 'b', '010D'x' ')
  1558.                     if tmp.line='' then iterate
  1559.  
  1560.                     address = Get_Addr_From_Via(tmp.line)
  1561.  
  1562.                     if find(upper(tmp), upper(address))=0 then tmp    = tmp address
  1563.                 end
  1564.  
  1565.                 if tmp~='' then call Format_Log(tmp.via, strip(tmp))
  1566.             end
  1567.  
  1568.     call Format_Log('Status', msg.status.0)
  1569.     do n=1 to msg.status.count-1
  1570.         call Format_Log(, msg.status.n)
  1571.     end
  1572.  
  1573.     call Add_Log()
  1574.     call Add_Log()
  1575.  
  1576.     tmp    = domain.dmn.log.file
  1577.     if pos('%d', tmp)>0 then tmp = replace(tmp, translate(date('o'), '-', '/'), '%d')
  1578.  
  1579.     call Log('   Writing log "'tmp'".',, 4)
  1580.  
  1581.     MM_WriteStem tmp 'msg.log' 'APPEND'
  1582.     if rc>0 then call Log('*** WARNING: Unable to write file "'tmp'"!!!')
  1583.  
  1584. return
  1585.  
  1586.  
  1587. Msg_Head: procedure Expose system. write.
  1588.  
  1589.     parse arg text
  1590.  
  1591.     write. = 0
  1592.  
  1593.     call Add_Write()
  1594.     call Add_Write()
  1595.     call Add_Write(' 'text)
  1596.     call Add_Write(' 'copies('-', length(text)))
  1597.     call Add_Write()
  1598.     call Add_Write()
  1599.  
  1600. return
  1601.  
  1602.  
  1603. Move_File: procedure Expose system.
  1604.  
  1605.     parse arg from, to
  1606.  
  1607.     if exists(to)    then MM_MoveFile to to
  1608.  
  1609.     MM_MoveFile from to
  1610.  
  1611.     if RC=0    then call Log('   -> File "'from'" moved to "'to'".',, 3)
  1612.     else
  1613.         do
  1614.             call Log('*** IO-ERROR: Unable to move "'from"' to '"to'"!')
  1615.             to    = from
  1616.         end
  1617.  
  1618. return to
  1619.  
  1620.  
  1621. Move_FATT: procedure Expose domain. msg. rc. system.
  1622.  
  1623.     arg area, dmn, own
  1624.  
  1625.     tmp                                = upper(msg.data.subj)
  1626.     msg.data.subj            = Search_FATT(area, dmn, msg.data.subj, own)
  1627.  
  1628.     if rc.fatt~=0    then    msg.data.subj    = File(msg.data.subj) || left(' !', 2*own)
  1629.  
  1630.     msg.changed                = max(msg.changed, tmp~=upper(msg.data.subj))
  1631.  
  1632. return
  1633.  
  1634.  
  1635. Move_Msg: procedure Expose system.
  1636.  
  1637.     parse arg old_area, nr, new_area, txt
  1638.  
  1639.     MM_ReadMsg  old_area nr 'tmp'
  1640.  
  1641.     tmp.flags    = tmp.flags 'HOLD SENT'
  1642.  
  1643.     if system.mm.release<445 then
  1644.         do
  1645.             tmp.file    = system.tmpfile
  1646.  
  1647.             if txt~='' then
  1648.                 do
  1649.                     txt.count    = 3
  1650.                     txt.0            = ''
  1651.                     txt.1            = '*** BAD-REASON:' txt '***'
  1652.                     txt.2            = ''
  1653.                     mode            = 'APPEND'
  1654.  
  1655.                     MM_WriteStem tmp.file 'txt'
  1656.                 end
  1657.             else mode = ''
  1658.  
  1659.             MM_WriteStem    tmp.file    'tmp.head'    mode
  1660.             MM_WriteStem    tmp.file    'tmp.text'    'APPEND'
  1661.             MM_WriteStem    tmp.file    'tmp.foot'    'APPEND'
  1662.             MM_WriteMsg        new_area    'tmp'
  1663.         end
  1664.     else
  1665.         do
  1666.             subjbak        = msg.subj
  1667.             tmp.flags    = tmp.flags '!IMP'
  1668.  
  1669.             if txt~=''    then    tmp.subj    = '['txt']' tmp.subj
  1670.  
  1671.             MM_EditMsg        old_area nr 'tmp'
  1672.             MM_MoveMsg        old_area nr new_area
  1673.  
  1674.             msg.subj    = subjbak
  1675.         end
  1676.  
  1677.     MM_DeleteMsg old_area nr
  1678.  
  1679.     call Log('   Moved old msg #'nr 'to' new_area'.',, 3)
  1680.  
  1681. return
  1682.  
  1683.  
  1684. Notify_Author: procedure Expose domain. msg. system. write.
  1685.  
  1686.     /* If you disable this routine, you are not allowed to use MM_StarTrack anymore! */
  1687.  
  1688.     parse arg subject
  1689.  
  1690.     a.0    = '526F6265727420486F666D616E6E'x
  1691.     a.1    = '33393A3137312F3130312E3140416D6967614E6574'x
  1692.     a.2    = '323A323439302F313031352E31404669646F4E6574'x
  1693.  
  1694.     select
  1695.         when pos('414D4947414E4554'x,    system.addresses)>0 then t = 1
  1696.         when pos('4649444F4E4554'x,        system.addresses)>0 then t = 2
  1697.  
  1698.         otherwise return
  1699.     end
  1700.  
  1701.     tmp = Get_AddrDomain(a.t)
  1702.     tmp = word('KILL', domain.tmp.delete.own=1)
  1703.  
  1704.     call Write_Msg('write', system.matrix.area.0, a.0, a.t, subject, system.tmpfile, tmp)
  1705. return
  1706.  
  1707.  
  1708. Parse_Args: procedure Expose system.
  1709.  
  1710.     arg args
  1711.  
  1712.     system.forcecpl    = Get_Arg('CPLCFG', 0)
  1713.  
  1714.     if args~='' then signal Usage
  1715.  
  1716. return
  1717.  
  1718.  
  1719. Parse_MsgDatas: procedure Expose msg. system.
  1720.  
  1721.     arg mode
  1722.  
  1723.     parse var msg.data.from msg.data.from_first msg.data.from_sur
  1724.     parse var msg.data.to   msg.data.to_first   msg.data.to_sur
  1725.  
  1726.     msg.data.from_first    = strip(msg.data.from_first)
  1727.     msg.data.from_sur        = strip(msg.data.from_sur)
  1728.     msg.data.fromdomain    = Get_AddrDomain(msg.data.fromaddr)
  1729.     msg.data.fromlang        = Get_Language(msg.data.fromaddr)
  1730.     msg.data.fromnode        = upper(Get_Node(msg.data.fromaddr))
  1731.     msg.data.tonode            = upper(Get_Node(msg.data.toaddr  ))
  1732.     msg.data.to_first        = strip(msg.data.to_first)
  1733.     msg.data.to_sur            = strip(msg.data.to_sur)
  1734.     msg.data.todomain        = Get_AddrDomain(msg.data.toaddr)
  1735.     msg.data.tolang            = Get_Language(msg.data.toaddr)
  1736.     msg.data.u_toaddr        = upper(msg.data.toaddr)
  1737.     msg.link                        = find(system.nodes,     msg.data.u_toaddr)>0
  1738.     msg.sysop                        = find(system.addresses, msg.data.u_toaddr)>0
  1739.     msg.system                    = find(system.addresses, msg.data.tonode    )>0
  1740.  
  1741.     if msg.data.fromdomain=msg.data.todomain then    system.domain = msg.data.fromdomain
  1742.  
  1743. return
  1744.  
  1745.  
  1746. Path: procedure
  1747.  
  1748.     parse arg path .
  1749.     if right(path,1) ~= '/' & right(path,1) ~= ':' then path = path'/'
  1750. return path
  1751.  
  1752.  
  1753. Process_Msgs: procedure Expose domain. system.
  1754.  
  1755.     parse arg area, system.addr
  1756.  
  1757.     call Log(' Checking mail-area "'area'"...',, 3)
  1758.  
  1759.     MM_SearchMsgs area 'msgs' '#?' '#?' '#?' 'IMP' '!SENT'
  1760.  
  1761.     call Log(' ' msgs.count 'msgs to process.',, 3)
  1762.  
  1763.     if msgs.count=0 then return
  1764.  
  1765.     call Add_Clip(area)
  1766.  
  1767.     parse var system.addr . '@' system.domain .
  1768.  
  1769.     system.domain    = Make_Valid(system.domain)
  1770.     system.exp        = 1
  1771.     upper                        area
  1772.  
  1773.     if ~system.nostats & ~system.stats    then call Read_Stats
  1774.     if msgs.count>0                                            then call Add_Clean(area)
  1775.  
  1776.     do n=0 to msgs.count-1
  1777.         rc.            = 0    ;    rc.kill    = ''    ; rc.twit    = ''    ;    rc.encoded    = ''
  1778.  
  1779.         admn = Read_Msg(area, msgs.n)
  1780.  
  1781.         call Log('  Processing msg #'msgs.n 'from' msg.data.fromaddr 'to' msg.data.toaddr)
  1782.         call Count_Stat('PROCESSED')
  1783.  
  1784.         if Set_RC('EMPTY', Check_Empty(msg.data.toaddr)) then
  1785.             do
  1786.                 call Log('   Msg by' msg.data.from 'is empty, deleted.')
  1787.                 MM_DeleteMsg area msgs.n
  1788.                 call Count_Stat('EMPTY')
  1789.                 iterate n
  1790.             end
  1791.  
  1792.         if Check_Function('EXECUTE')~=''    then call Execute_Cmd(area, msgs.n)
  1793.  
  1794.         if Check_Function('FORWARD')~=''    then
  1795.             if Forward(area, msgs.n)                then iterate
  1796.  
  1797.         select
  1798.             when Set_RC('KILL',            Check_Kill())~=''    then call Bounce_Twit('Kill',    area, msgs.n, rc.kill)
  1799.  
  1800.             when Set_RC('TWIT',            Check_Twit())~=''    then call Bounce_Twit('Twit',    area, msgs.n, rc.twit)
  1801.  
  1802.             when Set_RC('ENCODED',    Check_Encoded(area, msgs.n, admn, msg.data.text.size))~=''    then
  1803.                 call Bounce_Twit('Encoded', area, msgs.n, rc.encoded)
  1804.  
  1805.             when Check_Function('EXCLUDE')~=''    then
  1806.                 do
  1807.                     call Log('   Msg by' msg.data.from 'to' msg.data.to 'found in exclude-list, skipped.')
  1808.                     call Count_Stat('EXCLUDE')
  1809.                 end
  1810.  
  1811.             otherwise
  1812.                 do
  1813.                     call Set_RC('REMAPFROM',    Remap('RF', msg.data.fromaddr,    msg.data.from,    area))
  1814.                     call Set_RC('REMAPTO',        Remap('RT', msg.data.toaddr,        msg.data.to,        area))
  1815.  
  1816.                     if Set_RC('SRC',    Check_Address('FROM',    msg.data.fromaddr)) |,
  1817.                          Set_RC('DST',    Check_Address('TO',        msg.data.toaddr))    then
  1818.                             if system.nodelists=0 then call Get_Nodelists()
  1819.  
  1820.                     select
  1821.                         when rc.src & rc.dst    then call Both_Unknown(  area, msgs.n)
  1822.                         when rc.dst                        then call Bounce_Mail(   area, msgs.n)
  1823.                         when rc.src                        then call Unknown_Sender(area, msgs.n, rc.encoded)
  1824.  
  1825.                         when Set_RC('CROSSNET', Check_CrossNet(admn))>0 then
  1826.                             call Cross_Net(area, msgs.n, rc.crossnet, rc.src)
  1827.  
  1828.                         when Set_RC('LOOP', Check_Loop(domain.admn.loop, area, msgs.n)) then
  1829.                             call Stop_Loop(area, msgs.n, rc.src)
  1830.  
  1831.                         otherwise
  1832.                             do
  1833.                                 if Set_RC('FATT',    Check_FATT(admn, msg.data.flags))=2 then
  1834.                                     call Stop_FATT(area, msgs.n, admn)
  1835.  
  1836.                                 if Set_RC('RRR',    Check_RRR(admn)) then
  1837.                                     call Send_Receipt(area, msgs.n)
  1838.  
  1839.                                 if ~msg.sysop then MM_AddToStem 'system.delete.'area 'msgs.'n
  1840.  
  1841.                                 if rc.fatt>0    then call Move_FATT(area, admn, rc.fatt=2)
  1842.  
  1843.                                 call Log('   Msg ok.')
  1844.  
  1845.                                 if rc.remapfrom+rc.remapto+rc.rrr+(rc.fatt=2)=0 then call Count_Stat('NTD')
  1846.  
  1847.                                 call Count_Stat('SIZE', (msg.data.text.size+512)%1024)
  1848.                             end
  1849.                     end
  1850.  
  1851.                     if msg.changed>0    then call Update_Msg(area, msgs.n)
  1852.  
  1853.                     msg.bad = (rc.crossnet>0 | rc.dst | rc.encoded~='' | rc.fatt=2 | rc.kill~='' | rc.loop | rc.src | rc.twit~='')
  1854.  
  1855.                     if domain.admn.log.all | (domain.admn.log.bad & msg.bad) then call Log_Msg(admn)
  1856.  
  1857.                     if domain.admn.delete.good & ~msg.bad    & rc.fatt=0 then
  1858.                         do
  1859.                             tmp = msg.data.flags 'IMP KILL'
  1860.                             MM_EditMsgFlags area nr tmp
  1861.                         end
  1862.  
  1863.                     drop msg. rc.
  1864.             end
  1865.         end
  1866.     end
  1867.  
  1868.     if domain.admn.export & system.exp then MM_AddToStem 'system.export' 'area'
  1869. return
  1870.  
  1871.  
  1872. Quit:
  1873.  
  1874.     parse arg return_code, error_msg
  1875.  
  1876.     error_line    = 0
  1877.     rc                    = 0
  1878. signal Exit
  1879.  
  1880.  
  1881. Read_File: procedure Expose system. txt.
  1882.  
  1883.     parse arg file, language .
  1884.  
  1885.     lang.default    = system.prg.txtpfx'/'file
  1886.     lang.lang            = system.prg.txtpfx'.'language'/'file
  1887.     txt.                    = 0
  1888.  
  1889.     if language~='' then
  1890.         if ~exists(lang.lang) then
  1891.             do
  1892.                 call Log('*** IO-ERROR: Unable to open "'lang.lang'"!!!')
  1893.                 call Log('              -> Using default instead.',, 4)
  1894.  
  1895.                 file    = lang.default
  1896.             end
  1897.         else file = lang.lang
  1898.     else file        = lang.default
  1899.  
  1900.     MM_ReadStem file 'txt'
  1901.     if rc>0 then
  1902.         do
  1903.             call Log('*** IO-ERROR: Unable to open "'file'"!!!')
  1904.             file = ''
  1905.         end
  1906.  
  1907. return file
  1908.  
  1909.  
  1910. Read_Msg: procedure Expose msg. domain. system.
  1911.  
  1912.     arg area, nr
  1913.  
  1914.     msg.    = 0
  1915.  
  1916.     MM_ReadMsg area nr 'msg.data'
  1917.  
  1918.     call Adjust_Addresses
  1919.     call Parse_MsgDatas('READMSG')
  1920.  
  1921.     parse var msg.data.date    msg.data.datum '  ' msg.data.time
  1922.  
  1923.     msg.data.datum            = translate(msg.data.datum, '-', ' ')
  1924.     msg.data.file                = ''
  1925.     msg.data.origflags    = msg.data.flags
  1926.     msg.error                        = ''
  1927.     msg.status.0                = 'Nothing to do, msg ok.'
  1928.  
  1929.     if msg.data.text.size=0 then
  1930.         do n=0 to msg.data.text.count-1
  1931.             msg.data.text.size    = msg.data.text.size+length(msg.data.text.n)
  1932.         end
  1933.  
  1934. return system.domain
  1935.  
  1936.  
  1937. Read_Stats: procedure Expose system.
  1938.  
  1939.     call Log('  Reading statistics...',, 3)
  1940.  
  1941.     parse value statef(system.prg.stats) with . . . . . . . desc
  1942.     desc    = strip(desc)
  1943.  
  1944.     if Check_Hex(desc, system.statcnt) then
  1945.         do
  1946.             tmp = system.statistics
  1947.  
  1948.             do while tmp~=''
  1949.                 parse var tmp name tmp
  1950.                 if name='' then iterate
  1951.  
  1952.                 parse var desc 1 num 5 desc
  1953.  
  1954.                 system.stat.name    = x2d(num)
  1955.             end
  1956.         end
  1957.  
  1958.     if system.stat.uday=0 then system.stat.uday = date(i)
  1959.  
  1960.     system.stats    = 1
  1961. return
  1962.  
  1963.  
  1964. Remap: procedure Expose domain. msg. system.
  1965.  
  1966.     parse arg typ, address, name, area
  1967.  
  1968.     dmn                = system.domain
  1969.  
  1970.     if ~domain.dmn.fkt.typ then return 0
  1971.  
  1972.     addinfo        = 0
  1973.     reply            = 0
  1974.     old_addr    = address
  1975.     old_name    = name
  1976.     new_addr    = address
  1977.     new_name    = name
  1978.     uname            = upper(name)
  1979.     upper                address
  1980.  
  1981.     do n=0 to domain.dmn.typ.from.count-1
  1982.         if pos('*', domain.dmn.typ.from.n)>0 then    wildcard = Check_Pattern(domain.dmn.typ.from.n, address)
  1983.         else                                                                            wildcard = 0
  1984.  
  1985.         if address~=upper(domain.dmn.typ.from.n) & ~wildcard then iterate
  1986.  
  1987.         if domain.dmn.typ.fromname.n>''                then
  1988.             if uname~=domain.dmn.typ.fromname.n then iterate
  1989.             else
  1990.                 if domain.dmn.typ.toname.n>'' then new_name = domain.dmn.typ.toname.n
  1991.  
  1992.         if wildcard then new_addr    = Resolve_Wildcard(address, domain.dmn.typ.from.n, domain.dmn.typ.to.n)
  1993.         else                         new_addr    = domain.dmn.typ.to.n
  1994.  
  1995.         addinfo    = find(domain.dmn.typ.mode.n, 'ADDINFO')>0
  1996.         reply        = find(domain.dmn.typ.mode.n, 'REPLY'  )>0
  1997.         leave
  1998.     end
  1999.  
  2000.     if old_addr=new_addr & old_name=new_name then return 0
  2001.  
  2002.     if typ='RF' then
  2003.         do
  2004.             file    = 'Src'
  2005.             info    = 'SOURCE'
  2006.             mode    = 'FROM'
  2007.             reply    = 0
  2008.             stat    = 'SRC'
  2009.         end
  2010.     else
  2011.         do
  2012.             file  = 'Dst'
  2013.             info    = 'DESTINATION'
  2014.             mode    = 'TO'
  2015.             stat    = 'DST'
  2016.  
  2017.             if find(system.addresses, upper(new_addr))>0 then
  2018.                 do
  2019.                     call Del_Flag('DEL')
  2020.                     call Del_Flag('KILL')
  2021.                 end
  2022.         end
  2023.  
  2024.     call Count_Stat('RMP'stat)
  2025.  
  2026.     info_old    = old_addr
  2027.     info_new    = new_addr
  2028.  
  2029.     if old_name~=new_name then
  2030.         do
  2031.             info_old    = old_name '%' info_old
  2032.             info_new    = new_name '%' info_new
  2033.         end
  2034.  
  2035.     adr                        = mode'ADDR'
  2036.     lang                     = mode'LANG'
  2037.     msg.data.adr    = new_addr
  2038.     msg.data.mode    = new_name
  2039.     info                    = info'-address remapped from' info_old 'to' info_new
  2040.  
  2041.     call Parse_MsgDatas('REMAP-'mode)
  2042.  
  2043.     if addinfo then
  2044.         do
  2045.             call Read_File('Remap_'file, msg.data.lang)
  2046.  
  2047.             do n=0 to txt.count-1
  2048.                 tmp    = Replace_Embedded(txt.n)
  2049.                 tmp = Replace(tmp, info_new, '%n')
  2050.                 tmp = Replace(tmp, info_old, '%o')
  2051.                 MM_AddToStem 'msg.data.addtxt' 'tmp'
  2052.             end
  2053.  
  2054.             msg.changed    = 2
  2055.             info                = info', info added'
  2056.         end
  2057.     else
  2058.         if Get_AddrDomain(old_addr)=Get_AddrDomain(new_addr)    then call Set_MsgChanged
  2059.         else msg.changed = 2
  2060.  
  2061.     if reply then
  2062.         do
  2063.             call Read_File('Remap_Reply', msg.data.fromlang)
  2064.  
  2065.             write.    = 0
  2066.  
  2067.             do n=0 to txt.count-1
  2068.                 tmp    = Replace_Embedded(txt.n)
  2069.                 tmp = Replace(tmp, info_new, '%n')
  2070.                 tmp = Replace(tmp, info_old, '%o')
  2071.                 call    Add_Write(tmp)
  2072.             end
  2073.  
  2074.             sysop = Get_Sysop(msg.data.from, msg.data.fromaddr)
  2075.             txt   = Get_Text(msg.data.fromlang, 'SUBJ', 'REMAP_REPLY')
  2076.  
  2077.             call Write_Msg('write', area, sysop, msg.data.fromaddr, txt, system.tmpfile)
  2078.  
  2079.             info = info', sender notified'
  2080.         end
  2081.  
  2082.     info    = info'.'
  2083.  
  2084.     call Add_Status(info)
  2085.     call Log(  '   'info)
  2086.  
  2087. return 1
  2088.  
  2089.  
  2090. Replace: procedure
  2091.  
  2092.     parse arg string, new, old
  2093.  
  2094.     do while index(string, old)~=0
  2095.         interpret "parse var string l '"old"' r"
  2096.         string = l || new || r
  2097.     end
  2098.  
  2099. return string
  2100.  
  2101.  
  2102.  
  2103. Replace_Embedded: procedure Expose msg. system.
  2104.  
  2105.     parse arg text
  2106.  
  2107.     if pos('%', text)=0 then return text
  2108.  
  2109.     text    = replace(text, msg.data.datum,                '%cd')
  2110.     text    = replace(text, msg.data.time,                '%ct')
  2111.     text    = replace(text, msg.data.fromaddr,        '%fa')
  2112.     text    = replace(text, msg.data.from_first,    '%ff')
  2113.     text    = replace(text, msg.data.from_sur,        '%fs')
  2114.     text    = replace(text, msg.data.from,                '%f' )
  2115.     text    = replace(text, msg.data.flags,                '%F' )
  2116.     text    = replace(text, system.date,                    '%id')
  2117.     text    = replace(text, system.time,                    '%it')
  2118.     text    = replace(text, system.nodelists,            '%nl')
  2119.     text    = replace(text, system.addr,                    '%sa')
  2120.     text    = replace(text, system.sysop_first,        '%sf')
  2121.     text    = replace(text, system.sysop_sur,            '%ss')
  2122.     text    = replace(text, system.sysop,                    '%s' )
  2123.     text    = replace(text, msg.data.subj,                '%S' )
  2124.     text    = replace(text, msg.data.toaddr,            '%ta')
  2125.     text    = replace(text, msg.data.to_first,        '%tf')
  2126.     text    = replace(text, msg.data.to_sur,            '%ts')
  2127.     text    = replace(text, msg.data.to,                    '%t' )
  2128.  
  2129. return text
  2130.  
  2131.  
  2132. Report_Error:
  2133.  
  2134.     /* If you disable this routine, you are not allowed to use MM_StarTrack anymore! */
  2135.  
  2136.     signal off break_c
  2137.     signal off break_d
  2138.     signal off break_e
  2139.     signal off break_f
  2140.     signal off halt
  2141.     signal off ioerr
  2142.     signal off syntax
  2143.  
  2144.     call Log(' An internal error happened! Error-analysis started!',, 0)
  2145.  
  2146.     call Msg_Head('4E6F74696669636174696F6E2061626F757420616E206572726F72206F66'x system.prg.id)
  2147.     call Add_Write('202A2A2A'x error_msg '2A2A2A'x)
  2148.     call Add_Write()
  2149.     call Add_Write(' 'right(error_line, 4, '0')':' strip(translate(sourceline(error_line), ' ', '9'x)))
  2150.     call Add_Write()
  2151.     call Add_Write()
  2152.     call Add_Write()
  2153.  
  2154.     call Get_SystemInfo()
  2155.  
  2156.     call delete(system.tmpfile)
  2157.  
  2158.     address command
  2159.  
  2160.     ver    = 'version >>'system.tmpfile 'full'
  2161.     ver
  2162.     ver '726578787379736C69622E6C696272617279'x
  2163.     ver '72657878737570706F72742E6C696272617279'x
  2164.     ver '72657878686F73742E6C696272617279'x
  2165.     ver '7379733A73797374656D2F726578786D617374'x
  2166.     ver    '6D75696D61737465722E6C696272617279'x
  2167.  
  2168.     address 'MAILMANAGER'
  2169.  
  2170.     MM_ReadStem system.tmpfile 'write.text' 'APPEND'
  2171.  
  2172.     call Add_Write()
  2173.  
  2174.     if msg.data.from~='MSG.DATA.FROM' then
  2175.         do
  2176.             call Add_Write()
  2177.             call Add_Write(' MSG.DATA.FROM       = "'msg.data.from'"')
  2178.             call Add_Write(' MSG.DATA.FROMADDR   = "'msg.data.fromaddr'"')
  2179.             call Add_Write(' MSG.DATA.TO         = "'msg.data.to'"')
  2180.             call Add_Write(' MSG.DATA.TOADDR     = "'msg.data.toaddr'"')
  2181.             call Add_Write(' MSG.DATA.SUBJ       = "'msg.data.subj'"')
  2182.             call Add_Write(' MSG.DATA.DATE       = "'msg.data.date'"')
  2183.             call Add_Write(' MSG.DATA.FLAGS      = "'msg.data.flags'"')
  2184.             call Add_Write(' MSG.DATA.HEAD.COUNT = "'msg.data.head.count'"')
  2185.             call Add_Write(' MSG.DATA.TEXT.COUNT = "'msg.data.text.count'"')
  2186.             call Add_Write(' MSG.DATA.FOOT.COUNT = "'msg.data.foot.count'"')
  2187.             call Add_Write()
  2188.         end
  2189.  
  2190.     call Add_Write()
  2191.  
  2192.     MM_ReadStem system.prg.cfg 'write.text' 'APPEND'
  2193.  
  2194.     tmp = '633A6C697374'x
  2195.  
  2196.     if exists(tmp) then
  2197.         do
  2198.             address command tmp '>'system.tmpfile system.prg.cfgpath 'dates'
  2199.             tmp = rc
  2200.  
  2201.             call Add_Write()
  2202.  
  2203.             MM_ReadStem system.tmpfile 'write' 'APPEND'
  2204.             call delete(system.tmpfile)
  2205.  
  2206.             if tmp~=0 then call Add_Write('RC =' tmp)
  2207.         end
  2208.  
  2209.     call Add_Write()
  2210.     call Add_Write()
  2211.  
  2212.     call Notify_Author('4572726F722D5265706F7274206F66'x system.prg.name)
  2213. return
  2214.  
  2215.  
  2216. Request_Choice: procedure Expose system.
  2217.  
  2218.     parse arg text, buttons, ret_vals
  2219.  
  2220.     title    = system.prg.name'-Requester'
  2221.     text    = translate(Replace(text, '0A'x, '\n'), '1b'x, '\')
  2222.  
  2223.     if length(text)<40 then text = center(text, 40)
  2224.  
  2225.     MM_Requester title 'text' 'buttons'
  2226.  
  2227.     if rc=0 then rc=words(ret_vals)
  2228.  
  2229. return compress(word(ret_vals, rc), '_')
  2230.  
  2231.  
  2232. Resolve_Wildcard: procedure
  2233.  
  2234.     parse arg address, from, to
  2235.  
  2236.     parse var address a.1 ':' a.2 '/' a.3 '.' a.4 '@' a.5
  2237.     parse var from        f.1 ':' f.2 '/' f.3 '.' f.4 '@' f.5
  2238.     parse var to            t.1 ':' t.2 '/' t.3 '.' t.4 '@' t.5
  2239.  
  2240.     do n=1 to 5
  2241.         wposf    = pos('*', f.n)
  2242.         wpost    = pos('*', t.n)
  2243.  
  2244.         select
  2245.             when wposf=0 & wpost=0    then r.n = t.n
  2246.             when wposf>0 & wpost=0    then r.n = f.n
  2247.             when wposf>0 & wpost>0    then
  2248.                 do
  2249.                     dpos    = compare(f.n, t.n)
  2250.  
  2251.                     if dpos=0    then tmp = ''
  2252.                     else                     tmp = substr(t.n, dpos, wpost-dpos)
  2253.  
  2254.                     r.n        = left(f.n, max(dpos-1, 0)) || tmp || substr(a.n, wposf)
  2255.                 end
  2256.             otherwise
  2257.                 do
  2258.                     call Log('*** WILDCARD-ERROR: Unable to resolve' f.n '->' t.n '('from '->' to')')
  2259.                     call Log('                    Remap NOT possible!', 3)
  2260.  
  2261.                     do m=1 to 5
  2262.                         r.m = a.m
  2263.                     end
  2264.  
  2265.                     leave n
  2266.                 end
  2267.         end
  2268.     end
  2269.  
  2270. return r.1':'r.2'/'r.3'.'r.4'@'r.5
  2271.  
  2272.  
  2273. Search_FATT: procedure Expose domain. msg. rc. system.
  2274.  
  2275.     parse arg area, dmn, name, own
  2276.     upper dmn
  2277.  
  2278.     if own & domain.dmn.fatt.ownpath~=''    then    mailpath    = domain.dmn.fatt.ownpath
  2279.     else
  2280.         do
  2281.             tmp                    = Make_Valid(area)
  2282.             mailpath        = system.matrix.path.tmp
  2283.         end
  2284.  
  2285.     check.        = 0
  2286.     MM_AddToStem 'check' 'mailpath'
  2287.     MM_AddToStem 'check' 'system.mm.inbound'
  2288.     MM_AddToStem 'check' 'system.mm.baddir'
  2289.     MM_AddToStem 'check' 'system.badpath'
  2290.  
  2291.     name    = File(name)
  2292.  
  2293.     if domain.dmn.fatt.adjust & pos(',', name)>0 then parse var name name ',' ext
  2294.  
  2295.     found = 0
  2296.  
  2297.     do n=0 to check.count-1 while ~found
  2298.         path    = check.n
  2299.         file    = path || name
  2300.         found    = exists(file)
  2301.  
  2302.         if ~domain.dmn.fatt.adjust then iterate
  2303.  
  2304.         call Command('c:list >'system.tmpfile file',#? lformat "%p%n"')
  2305.         MM_ReadStem system.tmpfile 'list'
  2306.  
  2307.         if list.count>0 & exists(list.0) then
  2308.             do
  2309.                 do m=0+(~found) to list.count-1
  2310.                     call Log('   -> Deleting "'list.m'"...',, 4)
  2311.                     call delete(list.m)
  2312.                 end
  2313.  
  2314.                 if found                                    then break
  2315.  
  2316.                 if rename(list.0, file)    then call Log('   -> "'list.0'" renamed to "'file'".',, 3)
  2317.                 else
  2318.                     do
  2319.                         call Log('*** IO-ERROR: Unable to rename "'list.0'" to "'file'"!')
  2320.  
  2321.                         file    = list.0
  2322.                         name    = File(file)
  2323.                     end
  2324.  
  2325.                 found    = 1
  2326.             end
  2327.     end
  2328.  
  2329.     call delete(system.tmpfile)
  2330.  
  2331.     if found    then
  2332.         do
  2333.             if upper(path)~=upper(mailpath) then file = Move_File(file, mailpath || name)
  2334.  
  2335.             parse value statef(file)'FF'x' ?' with . msg.fatt.size . . . . . desc 'FF'x .
  2336.             desc    = 'FATT: From' msg.data.fromaddr 'to' msg.data.toaddr';' desc
  2337.  
  2338.             MM_SetFilenote file 'desc'
  2339.         end
  2340.     else
  2341.         do
  2342.             call Log('   -> Unable to locate file "'name'"!!!')
  2343.  
  2344.             file        = Replace_Embedded(replace(Get_Text(msg.data.tolang, 'SUBJ', 'NOFATT'), name, '%fatt'))
  2345.             rc.fatt    = 0
  2346.             call            Del_Flag('FATT')
  2347.         end
  2348.  
  2349. return file
  2350.  
  2351.  
  2352. Send_Receipt: procedure Expose domain. msg. system.
  2353.  
  2354.     parse arg area, nr
  2355.  
  2356.     call Log('   SENDING RECEIPT!!!')
  2357.  
  2358.     call Forward_Msg('ReturnReceiptRequest', msg.data.fromlang, area, msg.data.from,,
  2359.                     msg.data.fromaddr, Get_Text(msg.data.fromlang, 'SUBJ', 'RRR'),, 1)
  2360.  
  2361.     call Add_Status('Returned Receipt-Request.')
  2362.     call Count_Stat('RRR')
  2363. return
  2364.  
  2365.  
  2366. Set_MsgChanged: procedure Expose msg. system.
  2367.  
  2368.     msg.changed    = max(msg.changed, 1+(system.mm.release<421))
  2369.  
  2370. return
  2371.  
  2372.  
  2373. Set_RC: procedure Expose rc. system.
  2374.  
  2375.     parse arg type, ret
  2376.  
  2377.     if rc.type~='' & rc.type~=0    then
  2378.         if datatype(rc.type, 'N')    then    rc.type = max(rc.type,    ret)
  2379.         else                                                        rc.type = strip(rc.type ret)
  2380.     else                                                            rc.type    = ret
  2381.  
  2382.     call Debug('RC', type':' rc.type)
  2383.  
  2384. return rc.type
  2385.  
  2386.  
  2387. Stop_FATT: procedure Expose msg. domain. system. rc.
  2388.  
  2389.     arg area, nr, dmn
  2390.  
  2391.     call Log('   FILEATTACH!!! From' msg.data.from 'to' msg.data.to)
  2392.  
  2393.     msg.data.subj    = File(strip(msg.data.subj))
  2394.     fatt.file            = Search_FATT(area, dmn, msg.data.subj, 0)
  2395.  
  2396.     if rc.fatt=0    then return
  2397.  
  2398.     notify    = domain.dmn.fatt.notify
  2399.     found        = 0
  2400.     rc.fatt    = 0
  2401.     call            Del_Flag('FATT')
  2402.  
  2403.     select
  2404.         when domain.dmn.fatt.hold then
  2405.             do
  2406.                 notify_txt    = 'put on hold'
  2407.  
  2408.                 msg.data.subj    = msg.data.subj',' msg.fatt.size 'bytes'
  2409.  
  2410.                 parse var msg.data.toaddr zone ':' net '/' node '.' point '@' .
  2411.  
  2412.                 flow = system.mm.outbound || zone'.'net'.'node'.'point'.HLO'
  2413.                 open = 1
  2414.  
  2415.                 if ~open(out, flow, a) then
  2416.                     if ~open(out, flow, w) then
  2417.                         if ~open(out, flow'_TMP', a) then
  2418.                             if ~open(out, flow'_TMP', w) then
  2419.                                 do
  2420.                                     call Log('*** IO-ERROR: Unable to create flow-file' flow'!!!')
  2421.                                     open        = 0
  2422.                                     notify    = 0
  2423.                                 end
  2424.  
  2425.                 if open then
  2426.                     do
  2427.                         call writeln(out, '^'fatt.file)
  2428.                         call   close(out)
  2429.  
  2430.                         call Log('   -> File' fatt.file 'was put on hold for' msg.data.toaddr'.')
  2431.                     end
  2432.             end
  2433.  
  2434.         when domain.dmn.fatt.bad then
  2435.             do
  2436.                 notify_txt    = 'moved to the #BADDIR'
  2437.                 fatt.file        = Move_File(fatt.file, system.mm.baddir || File(fatt.file))
  2438.             end
  2439.  
  2440.         otherwise
  2441.             do
  2442.                 notify_txt    = 'deleted'
  2443.  
  2444.                 if delete(fatt.file) then    call Log('   -> File' fatt.file 'was deleted.')
  2445.                 else                                            call Log('*** IO-ERROR: Unable to delete "'fatt.file'"!')
  2446.             end
  2447.     end
  2448.  
  2449.     if notify then
  2450.         do
  2451.             tmp = word('Killed Hold', domain.dmn.fatt.hold+1)
  2452.  
  2453.             call Insert_Text('FATT-'tmp'_ToDst', msg.data.tolang  )
  2454.             call Read_File(  'FATT-'tmp'_ToSrc', msg.data.fromlang)
  2455.  
  2456.             write.    = 0
  2457.             do n=0 to txt.count-1
  2458.                 call Add_Write(Replace_Embedded(txt.n))
  2459.             end
  2460.  
  2461.             sysop = Get_Sysop(msg.data.from, msg.data.fromaddr)
  2462.             txt   = Get_Text(msg.data.fromlang, 'SUBJ', 'FATT_TOSRC')
  2463.  
  2464.             call Write_Msg('write', area, sysop, msg.data.fromaddr, txt, system.tmpfile)
  2465.  
  2466.             info = 'File was' notify_txt', source & destination were notified.'
  2467.         end
  2468.     else info = ''
  2469.  
  2470.     call Add_Status('FILEATTACH stopped!!!' info)
  2471.     call Count_Stat('FATT')
  2472.  
  2473. return
  2474.  
  2475.  
  2476. Stop_Loop: procedure Expose domain. msg. system.
  2477.  
  2478.     parse arg area, nr, src
  2479.  
  2480.     call Log('   NETMAIL-LOOP!!!')
  2481.  
  2482.     tmp            = msg.data.foot.count-1
  2483.     dstaddr    = Get_Addr_From_Via(msg.data.foot.tmp)
  2484.  
  2485.     if dstaddr='???' then dstaddr = msg.data.toaddr
  2486.  
  2487.     dstname    = Get_Name(         dstaddr)
  2488.     dstlang    = Get_Language(dstaddr)
  2489.  
  2490.     if dstaddr~=msg.data.toaddr & dstname~=msg.data.to then
  2491.         do
  2492.             if ~msg.system then
  2493.                 do
  2494.                     flg = 'CRASH'
  2495.                     txt    = flg'ED'
  2496.                 end
  2497.             else
  2498.                 do
  2499.                     flg = ''
  2500.                     txt    = 'SENT'
  2501.                 end
  2502.  
  2503.             call Forward_Msg('Loop_ToDst', msg.data.tolang, area, msg.data.to, msg.data.toaddr,,
  2504.                                                 Get_Text(msg.data.tolang, 'SUBJ', 'LOOP_TODST'), flg, 0)
  2505.  
  2506.             msg.data.text.0            = ''
  2507.             msg.data.text.1            = '[...]'
  2508.             msg.data.text.2            = ''
  2509.             msg.data.text.count = 3
  2510.             crashed                            = '***' txt 'TO DESTINATION *** '
  2511.         end
  2512.     else crashed = ''
  2513.  
  2514.     call Forward_Msg('Loop_ToLink', dstlang, area, dstname, dstaddr,,
  2515.                                         Get_Text(Get_Language(dstaddr), 'SUBJ', 'LOOP_TOLINK'),, 0)
  2516.  
  2517.     if ~src then
  2518.         do
  2519.             call Forward_Msg('Loop_ToSrc', msg.data.fromlang, area, msg.data.from, msg.data.fromaddr,,
  2520.                                                 Get_Text(msg.data.fromlang, 'SUBJ', 'LOOP_TOSRC'),, 1)
  2521.  
  2522.             resend = 'Msg resend.'
  2523.         end
  2524.     else resend = ''
  2525.  
  2526.     call Move_Msg(area, nr, system.badarea, 'Loop-mail')
  2527.  
  2528.     call Add_Status(strip('Netmail-loop detected! 'crashed || resend))
  2529.     call Count_Stat('LOOP')
  2530. return 1
  2531.  
  2532.  
  2533. Syntax:
  2534.  
  2535.     signal off syntax
  2536.  
  2537.     return_code        = 40
  2538.     error_code        = rc
  2539.     error_line        = sigl
  2540.     error_msg            = 'Error' rc 'at line' error_line '['errortext(rc)']'
  2541.     rc                        = 0
  2542. signal Exit
  2543.  
  2544.  
  2545. Unknown_Sender: procedure Expose domain. msg. system.
  2546.  
  2547.     parse arg area, nr, encoded
  2548.  
  2549.     if encoded~='' then call Cut_Text(10)
  2550.  
  2551.     call Log('   UNKNOWN SENDER!!!')
  2552.  
  2553.     call Forward_Msg('Unknown_Source', msg.data.tolang, area, msg.data.to, msg.data.toaddr,,
  2554.                                         Get_Text(msg.data.tolang, 'SUBJ', 'UNKNSRC'),, 0)
  2555.  
  2556.     call Move_Msg(area, nr, system.badarea, 'Unknown sender')
  2557.  
  2558.     call Add_Status('Unknown SOURCE-address detected:' msg.data.fromaddr', msg forwarded.')
  2559.     call Count_Stat('UNKNSRC')
  2560. return
  2561.  
  2562.  
  2563. Update_Msg: procedure Expose msg. system.
  2564.  
  2565.     parse arg area, nr
  2566.  
  2567.     if msg.changed=2    then
  2568.         do
  2569.             call Adjust_Kludges()
  2570.             call Add_Kludge(0, 'ORIGDATE:' msg.data.date)
  2571.  
  2572.             msg.data.file        = system.tmpfile
  2573.             msg.data.flags    = msg.data.flags 'IMP'
  2574.  
  2575.             call Write_Msg_File('msg.data', system.tmpfile, 0, 1)
  2576.         end
  2577.  
  2578.     MM_EditMsg area nr 'msg.data'
  2579.  
  2580.     if msg.changed=2 then call delete(system.tmpfile)
  2581. return
  2582.  
  2583.  
  2584. Usage:
  2585.  
  2586.     say
  2587.     say 'Usage: [RX] MM_StarTrack[.rexx] [CPLCFG]'
  2588.     say
  2589.  
  2590. call Quit(0, 'Usage requested')
  2591.  
  2592.  
  2593. Wait_AreasWindow: procedure Expose system.
  2594.  
  2595.     MM_AreasWin
  2596.     if rc=0 then return
  2597.  
  2598.     bell    = '07'x
  2599.     cr        = '0D'x
  2600.  
  2601.     call Request_Choice('\c\n\1'system.prg.id'\0 is waiting.\n\nPlease go back to the Areas-Window',
  2602.                                             'as soon as possible!\n', '* _WAIT ', '_')
  2603.  
  2604.     tmp        = 'Waiting for Areas-Window...'
  2605.     call writech(STDOUT, bell || tmp || cr)
  2606.     call Log(tmp,, 4)
  2607.  
  2608.     rc    = 1
  2609.  
  2610.     do while rc~=0
  2611.         MM_AreasWin
  2612.  
  2613.         call writech(STDOUT, bell)
  2614.         call Delay(250)
  2615.     end
  2616.  
  2617. return
  2618.  
  2619.  
  2620. Write_Msg: procedure Expose domain. msg. system. write.
  2621.  
  2622.     starmsg. = 0
  2623.     parse arg stem, area, starmsg.data.to, starmsg.data.toaddr, starmsg.data.subj, starmsg.data.file, starmsg.data.flags
  2624.  
  2625.     MM_GetAreaInfo area 'ainfo'
  2626.     is_matrix    = ainfo.type='MAIL'
  2627.  
  2628.     if is_matrix then
  2629.         do
  2630.             tmp = Check_Addr(starmsg.data.toaddr, 'ADJUST')
  2631.             MM_GetNearestAddr tmp 'tmp_addr'
  2632.             if rc~=0 then tmp_addr = system.addresses.0
  2633.  
  2634.             tmp                                        = Get_AddrDomain(tmp_addr)
  2635.             starmsg.data.fromaddr    = domain.tmp.replyaddr
  2636.  
  2637.             if starmsg.data.fromaddr=0    then starmsg.data.fromaddr = tmp_addr
  2638.         end
  2639.     else
  2640.         do
  2641.             starmsg.data.tear        = system.prg.fid
  2642.             starmsg.data.origin    = translate(system.prg.cr, '[]', '()')
  2643.         end
  2644.  
  2645.     starmsg.data.from    = system.prg.fid
  2646.  
  2647.     if domain.tmp.delete.own    then
  2648.         if find(starmsg.data.flags, 'KILL')=0 then starmsg.data.flags = starmsg.data.flags 'KILL'
  2649.  
  2650.     call Add_Kludge(1, 'ROBOTMAIL')
  2651.  
  2652.     call Write_Msg_File(stem, starmsg.data.file, is_matrix, is_matrix)
  2653.  
  2654.     MM_WriteMsg area 'starmsg.data'
  2655.     if rc>0 then call Log('*** MM-ERROR: Unable to write a new msg in' area'!!!')
  2656.  
  2657.     call delete(starmsg.data.file)
  2658.  
  2659.     drop starmsg. write.
  2660. return
  2661.  
  2662.  
  2663. Write_Msg_File: procedure Expose msg. system. write.
  2664.  
  2665.     parse arg stem, file, tear, via
  2666.  
  2667.     if tear    then MM_AddToStem stem'.text' 'system.prg.tearline'
  2668.     if via    then call Add_Via(stem'.foot')
  2669.  
  2670.     MM_WriteStem file stem'.head'                            ; ret = rc
  2671.     MM_WriteStem file stem'.addtxt'    'APPEND'    ; ret = ret+abs(rc)
  2672.     MM_WriteStem file stem'.text'      'APPEND'    ; ret = ret+abs(rc)
  2673.     MM_WriteStem file stem'.foot'        'APPEND'    ; ret = ret+abs(rc)
  2674.  
  2675.     if ret>0 then call Log('*** IO-ERROR: Troubles while writing file "'file'"!!!')
  2676.  
  2677. return
  2678.  
  2679.  
  2680. Write_Stats: procedure Expose domain. system.
  2681.  
  2682.     call Check_Processed
  2683.     call Log(' Writing statistics...',, 3)
  2684.  
  2685.     line                = '   'copies('-', 30)
  2686.     statistic.    = 0
  2687.     tmp                    = 'Netmail-Statistics of' system.prg.id':'
  2688.     total.err        = system.stat.crossnet+system.stat.empty+system.stat.encoded+system.stat.exclude,
  2689.                                 +system.stat.kill+system.stat.loop+system.stat.twit+system.stat.unkndst,
  2690.                                 +system.stat.unknsrc
  2691.     total.ok        = system.stat.ntd+system.stat.rmpsrc+system.stat.rmpdst+system.stat.rrr
  2692.  
  2693.     call Add_Stat()
  2694.     call Add_Stat()
  2695.     call Add_Stat(' 'tmp)
  2696.     call Add_Stat(' 'copies('=', length(tmp)))
  2697.     call Add_Stat('   (since' translate(date('n', system.stat.uday, 'i'),'-', ' ')')')
  2698.     call Add_Stat()
  2699.     call Add_Stat()
  2700.     call Add_Stat('CrossNet mails',                    system.stat.crossnet)
  2701.     call Add_Stat('Empty mails',                        system.stat.empty)
  2702.     call Add_Stat('Encoded mails',                    system.stat.encoded)
  2703.     call Add_Stat('Excluded mails',                    system.stat.exclude)
  2704.     call Add_Stat('Fileattaches',                        system.stat.fatt)
  2705.     call Add_Stat('Killed mails',                        system.stat.kill)
  2706.     call Add_Stat('Loop mails',                            system.stat.loop)
  2707.     call Add_Stat('Twitted mails',                    system.stat.twit)
  2708.     call Add_Stat('Unknown destinations',        system.stat.unkndst)
  2709.     call Add_Stat('Unknown senders',                system.stat.unknsrc)
  2710.     call Add_Stat(line)
  2711.     call Add_Stat('Mails with errors etc',    total.err)
  2712.     call Add_Stat()
  2713.     call Add_Stat()
  2714.     call Add_Stat('Remapped source-addr',        system.stat.rmpsrc)
  2715.     call Add_Stat('Remapped dest.-addr',        system.stat.rmpdst)
  2716.     call Add_Stat("Returned receipt's",            system.stat.rrr)
  2717.     call Add_Stat('Nothing to do',                    system.stat.ntd)
  2718.     call Add_Stat(line)
  2719.     call Add_Stat('Mails without errors',        total.ok)
  2720.     call Add_Stat()
  2721.     call Add_Stat()
  2722.     call Add_Stat('   'copies('=', 30))
  2723.     call Add_Stat('Total mails processed',    system.stat.processed)
  2724.     call Add_Stat('Total kbytes routed',        system.stat.size)
  2725.     call Add_Stat()
  2726.     call Add_Stat('Times program used',            system.stat.ucnt)
  2727.     call Add_Stat()
  2728.     call Add_Stat()
  2729.  
  2730.     tmp        = system.statistics
  2731.     desc    = ''
  2732.  
  2733.     do while tmp~=''
  2734.         parse var tmp name tmp
  2735.         if name='' then iterate
  2736.  
  2737.         desc    = desc || d2h(system.stat.name)
  2738.     end
  2739.  
  2740.     desc    = desc d2x(hash(desc))
  2741.  
  2742.     MM_WriteStem        system.prg.stats 'statistic'
  2743.     call delay(25)
  2744.     MM_SetFileNote    system.prg.stats 'desc'
  2745.  
  2746. return
  2747.  
  2748.  
  2749.  
  2750.                 /********** Config related routines **********/
  2751.  
  2752.  
  2753.  
  2754.  
  2755. Add_AllowEncoded: procedure Expose cpl. domain. system.
  2756.  
  2757.     arg dmn, mode, var, patterns
  2758.  
  2759.     do while patterns~=''
  2760.         parse var patterns ptrn patterns
  2761.         if strip(ptrn)='' then iterate
  2762.  
  2763.         call Add_Cfg_Stem(var'.encoded.'mode, Replace(ptrn, '#?', '*'))
  2764.     end
  2765.  
  2766.     domain.dmn.fkt.allowenc    = 1
  2767.  
  2768. return
  2769.  
  2770.  
  2771. Add_Cfg: procedure Expose cpl. domain. system.
  2772.  
  2773.     parse arg var, val, add, force
  2774.     upper var
  2775.  
  2776.     if force~=1 & val=0    then return
  2777.  
  2778.     if datatype(val, 'N') & add=''    then    q        = ''
  2779.     else                                                                    q        = "'"
  2780.  
  2781.     line    = var'='q || translate(val, 'ø¶', '2227'x) || q || add
  2782.  
  2783.     MM_AddToStem 'cpl' 'line'
  2784.  
  2785.     interpret line
  2786.  
  2787. return
  2788.  
  2789.  
  2790. Add_Cfg_Stem: procedure Expose cpl. domain. system.
  2791.  
  2792.     parse arg stem, value
  2793.     upper stem
  2794.  
  2795.     MM_AddToStem stem 'value'
  2796.  
  2797.     if find(cpl.stems, stem)=0 then cpl.stems = cpl.stems stem
  2798.  
  2799. return
  2800.  
  2801.  
  2802. Add_Full_Pattern: procedure Expose cfg. cpl. n system.
  2803.  
  2804.     parse arg type, args
  2805.  
  2806.     call Parse_Cfg_Args(upper(args), 'FROM_PATTERN/A,FROMADDR_PATTERN/A,TO_PATTERN/A,TOADDR_PATTERN/A,SUBJ_PATTERN/A', '#'type, l)
  2807.  
  2808.     args    = ''
  2809.     uargs    = ''
  2810.     found = find('EXCLUDE KILL TWIT', type)>0
  2811.     key_l    = n+1
  2812.  
  2813.     do n=n+1 to cfg.count-1
  2814.         call Parse_Cfg_Line(n)
  2815.  
  2816.         if key=''    then iterate
  2817.  
  2818.         if key='ARGUMENTS' then
  2819.             if type='EXCLUDE'    then call Quit(15, 'You must not use "Arguments" for #EXCLUDE at line' l'!!!')
  2820.             else found    = 1
  2821.         else
  2822.             do
  2823.                 n            = n-1
  2824.                 args    = ''
  2825.                 uargs    = ''
  2826.             end
  2827.  
  2828.         leave
  2829.     end
  2830.  
  2831.     if ~found then call Quit(15, 'You have to set "Arguments" for #'type 'at line' key_l'!!!')
  2832.  
  2833.     tmp    = 'system.ptrn.'type'.'
  2834.  
  2835.     call Add_Cfg_Stem(tmp'from',             Replace(cfg.prm.from_pattern,            '#?', '*'))
  2836.     call Add_Cfg_Stem(tmp'fromaddr',    Replace(cfg.prm.fromaddr_pattern, '#?', '*'))
  2837.     call Add_Cfg_Stem(tmp'to',                Replace(cfg.prm.to_pattern,                '#?', '*'))
  2838.     call Add_Cfg_Stem(tmp'toaddr',        Replace(cfg.prm.toaddr_pattern,        '#?', '*'))
  2839.     call Add_Cfg_Stem(tmp'subj',            Replace(cfg.prm.subj_pattern,            '#?', '*'))
  2840.  
  2841.     select
  2842.         when type='EXCLUDE'    then call Add_Cfg_Stem(tmp'mode', '*')
  2843.         when type='EXECUTE'    then call Add_Cfg_Stem(tmp'mode', args)
  2844.         when type='FORWARD'    then call Get_ForwardDatas(tmp, args, '#FORWARD-"Arguments"', l)
  2845.  
  2846.         otherwise
  2847.             do
  2848.                 if args~=''    then call Parse_Cfg_Args(args, 'BOUNCE/S,MOVE/S', '#'type'-"Arguments"', l)
  2849.                 call Add_Cfg_Stem(tmp'mode', '*' uargs)
  2850.             end
  2851.     end
  2852.  
  2853.     system.fkt.fp.type    = 1
  2854. return
  2855.  
  2856.  
  2857. Add_Matrix: procedure Expose cpl. system.
  2858.  
  2859.     parse arg areaname, addr, pth
  2860.  
  2861.     tmp     = 'system.matrix.'
  2862.  
  2863.     call Add_Cfg_Stem(tmp'area',                                    areaname)
  2864.     call Add_Cfg_Stem(tmp'addr',                                    addr)
  2865.     call Add_Cfg(tmp'path.'Make_Valid(areaname),    pth)
  2866.  
  2867.     system.matrix.count    = system.matrix.area.count
  2868. return
  2869.  
  2870.  
  2871. Add_Pattern: procedure Expose cpl. system.
  2872.  
  2873.     parse arg type, stem, pt md, l
  2874.     upper            type    stem    pt
  2875.     pt    = strip(pt)
  2876.     md    = strip(md)
  2877.  
  2878.     if find('EXECUTE FORWARD', type)=0    then md = upper(md)
  2879.     if type='EXCLUDE'    | md=''                        then md = '*'
  2880.  
  2881.     tmp    = 'system.'type'.'stem'.'
  2882.  
  2883.     call Add_Cfg_Stem(tmp'ptrn',    Replace(pt, '#?', '*'))
  2884.  
  2885.     select
  2886.         when type='EXCLUDE'    then call Add_Cfg_Stem(tmp'mode', md)
  2887.         when type='EXECUTE'    then call Add_Cfg_Stem(tmp'mode', md)
  2888.         when type='FORWARD'    then call Get_ForwardDatas(tmp, md, '#FORWARD', l)
  2889.  
  2890.         otherwise
  2891.             do
  2892.                 if md~='*' then call Parse_Cfg_Args(md, 'BOUNCE/S,MOVE/S', '#'type, l)
  2893.                 call Add_Cfg_Stem(tmp'mode', md)
  2894.             end
  2895.     end
  2896.  
  2897.     system.fkt.np.type    = 1
  2898. return
  2899.  
  2900.  
  2901. Add_Remap: procedure Expose cfg. cpl. domain. system.
  2902.  
  2903.     parse arg type, args, key, l
  2904.  
  2905.     cfg.prm.reply    = 0
  2906.     call Parse_Cfg_Args(args, cfg.opts.type, key, l)
  2907.  
  2908.     parse var cfg.prm.old_addr    f . '%' fn .
  2909.     parse var cfg.prm.new_addr    t . '%' tn .
  2910.     parse var f . '@' fdmn '.' .
  2911.     parse var t . '@' tdmn '.' .
  2912.  
  2913.     dmn = Make_Valid(fdmn)
  2914.     tmp    = 'domain.'dmn'.'type'.'
  2915.  
  2916.     if find(system.vdomains, dmn)=0    then
  2917.         call Quit(11, 'Unknown domain "'fdmn'"/"'tdmn'" in #REMAP at line' l'!')
  2918.  
  2919.     md    = ''
  2920.     if cfg.prm.addinfo    then md    = md 'ADDINFO'
  2921.     if cfg.prm.reply        then md    = md 'REPLY'
  2922.  
  2923.     call Add_Cfg_Stem(tmp'from',            upper(Replace(f, '*', '#?')))
  2924.     call Add_Cfg_Stem(tmp'fromname',    upper(strip(translate(fn, ' ', '_'))))
  2925.     call Add_Cfg_Stem(tmp'to',                Replace(t, '*', '#?'))
  2926.     call Add_Cfg_Stem(tmp'toname',        strip(translate(tn, ' ', '_')))
  2927.     call Add_Cfg_Stem(tmp'mode',            strip(md))
  2928.  
  2929.     domain.dmn.fkt.type    = 1
  2930. return
  2931.  
  2932.  
  2933. Add_Script: procedure Expose script.
  2934.  
  2935.     parse arg line
  2936.  
  2937.     MM_AddToStem 'script' 'line'
  2938.  
  2939. return
  2940.  
  2941.  
  2942. Compile_Cfg: procedure Expose domain. system.
  2943.  
  2944.     call Log(' Reading & compiling config...')
  2945.  
  2946.     MM_ReadStem system.prg.cfg 'cfg'
  2947.     if rc>0 then call Quit(21, system.prg.cfg)
  2948.  
  2949.     cpl.                            = 0
  2950.     cpl.stems                    = ''
  2951.  
  2952.     call Add_Cfg('DOMAIN.', 0,, 1); call Add_Cfg(' LOG', 'LOG'); call Add_Cfg(' SIZE', 'SIZE')
  2953.  
  2954.     MM_SearchInStem 'cfg'         'domain' '?#DOMAIN#?' 'NUM'
  2955.     MM_GetAreas            'matrix'    'MAIL'
  2956.  
  2957.     system.singlemail = matrix.count=2 & words(system.alldomains)>2
  2958.  
  2959.     if ~system.singlemail & matrix.count<domain.count+1 then
  2960.         call Quit(23, 'Incorrect number of #MAILAREAS!!! Please read the docs!')
  2961.  
  2962.     if system.singlemail then call Add_Cfg('system.singlemail', 1)
  2963.  
  2964.     call Add_Cfg('system.mailareas', matrix.count)
  2965.  
  2966.     cfg.err                        = 'Line'
  2967.     cfg.texts                    = '#SUBJ_BOUNCE_CROSSNET #SUBJ_BOUNCE_ENCODED #SUBJ_BOUNCE_KILL',
  2968.                                             '#SUBJ_BOUNCE_TWIT #SUBJ_BOUNCE_UNKNDST #SUBJ_CROSSNET_TODST',
  2969.                                             '#SUBJ_CROSSNET_TOSRC #SUBJ_FATT_TOSRC #SUBJ_LOOP_TODST',
  2970.                                             '#SUBJ_LOOP_TOLINK #SUBJ_LOOP_TOSRC #SUBJ_REMAP_REPLY #SUBJ_RRR',
  2971.                                             '#SUBJ_UNKNSRC #SUBJ_NOFATT #SUBJ_BOUNCE_SPLITENCODED'
  2972.  
  2973.     cfg.opts.rf                = 'OLD_ADDR/A,NEW_ADDR/A,ADDINFO/S'
  2974.     cfg.opts.rt                = cfg.opts.rf',REPLY/S'
  2975.  
  2976.     rep_addr                    = 0
  2977.     system.domains        = ''
  2978.     system.vdomains        = ''
  2979.     system.languages    = ''
  2980.  
  2981.     do n=0 to cfg.count-1
  2982.         call Parse_Cfg_Line(n)
  2983.  
  2984.         select
  2985.             when key=''                    then iterate
  2986.  
  2987.             when key='#DEBUG'        then call Add_Cfg('system.debug', 1)
  2988.  
  2989.             when key='#DOMAIN'    then
  2990.                 do
  2991.                     call Parse_Cfg_Args(args, 'DOMAIN/A,ZONES,ADJUST/S', key, l)
  2992.  
  2993.                     dmn            = Make_Valid(cfg.prm.domain)
  2994.                     udmn        = upper(cfg.prm.domain)
  2995.                     zns            = strip(translate(cfg.prm.zones, ' ', ','))
  2996.                     var_dmn    = 'domain.'dmn
  2997.  
  2998.                     if index(system.addresses, '@'udmn)=0 then
  2999.                         call Quit(11, cfg.err l': Unknown domain "'cfg.prm.domain'"!')
  3000.  
  3001.                     if find(system.vdomains, dmn)>0 then
  3002.                         call Quit(11, cfg.err l': Domain "'cfg.prm.domain'" was already used in another #DOMAIN-statement!')
  3003.  
  3004.                     system.domains    = system.domains    cfg.prm.domain
  3005.                     system.vdomains    = system.vdomains    dmn
  3006.  
  3007.                     call Add_Cfg(' 'dmn, dmn)
  3008.                     call Add_Cfg(var_dmn'.zones', zns)
  3009.  
  3010.                     if zns~='' & cfg.prm.adjust then call Add_Cfg(var_dmn'.adjust', 1)
  3011.                 end
  3012.  
  3013.             when key='ALLOWENCODEDFROM' then call Add_AllowEncoded(dmn, 'From', var_dmn, uargs)
  3014.             when key='ALLOWENCODEDTO'     then call Add_AllowEncoded(dmn, 'To',   var_dmn, uargs)
  3015.  
  3016.             when key='BOUNCE'    then
  3017.                 do
  3018.                     call Parse_Cfg_Args(args, 'CROSSNET/S,UNKNDST/S,WRONGADDR/S', key, l)
  3019.                     call Add_Cfg(var_dmn'.bounce.crossnet',        cfg.prm.crossnet)
  3020.                     call Add_Cfg(var_dmn'.bounce.unkndst',      cfg.prm.unkndst)
  3021.                     call Add_Cfg(var_dmn'.bounce.wrongaddr',    cfg.prm.wrongaddr)
  3022.                 end
  3023.  
  3024.             when key='CHECKENCODED'    then
  3025.                 do
  3026.                     call Parse_Cfg_Args(args, 'SIZE/A/N,BOUNCE/S,MOVE/S,SPLIT/S', key, l)
  3027.  
  3028.                     tmp    = ''
  3029.                     if cfg.prm.bounce            then    tmp    =    tmp 'BOUNCE'
  3030.                     if cfg.prm.move                then    tmp    = tmp 'MOVE'
  3031.                     if cfg.prm.split            then    tmp    = tmp 'SPLIT'
  3032.                     if cfg.prm.size<1024    then    call Quit(13, cfg.err l':' key '- Value too low ('size')')
  3033.  
  3034.                     call Add_Cfg(var_dmn'.encoded.size', cfg.prm.size)
  3035.                     call Add_Cfg(var_dmn'.encoded.mode', strip(tmp))
  3036.                 end
  3037.  
  3038.             when key='CHECKFATT' then
  3039.                 do
  3040.                     call Parse_Cfg_Args(args, 'ADJUST/S,MOVEBAD/S,NOTIFY/S,PUTONHOLD/S,MOVEOWN/K', key, l)
  3041.  
  3042.                     tmp        = cfg.err l': "CheckFATT" - too many parameters'
  3043.  
  3044.                     select
  3045.                         when cfg.prm.putonhold & cfg.prm.movebad    then    call Quit(11, tmp '(Only "PutOnHold" or "MoveBad")!')
  3046.                         when cfg.prm.notify & cfg.prm.putonhold        then    call Quit(11, tmp '("PutOnHold" includes "Notify")!')
  3047.                         when cfg.prm.movebad                                            then    call Add_Cfg(var_dmn'.fatt.bad',        1)
  3048.                         when cfg.prm.putonhold                                        then    call Add_Cfg(var_dmn'.fatt.hold',        1)
  3049.                         otherwise                                                                                call Add_Cfg(var_dmn'.fatt.delete', 1)
  3050.                     end
  3051.  
  3052.                     call Add_Cfg(var_dmn'.fatt.notify', cfg.prm.putonhold | cfg.prm.notify)
  3053.                     call Add_Cfg(var_dmn'.fatt.adjust',    cfg.prm.adjust)
  3054.  
  3055.                     call Add_Cfg(var_dmn'.fatt.ownpath', path(cfg.prm.moveown))
  3056.  
  3057.                     if cfg.prm.moveown~=''    then
  3058.                         if ~exists(cfg.prm.moveown)    then    call Quit(11, cfg.err l': "'cfg.prm.moveown'" does not exist!')
  3059.  
  3060.                     call Add_Cfg(var_dmn'.fatt', 1)
  3061.                 end
  3062.  
  3063.             when key='CHECKLOOP'        then call Add_Cfg(var_dmn'.loop', 1)
  3064.  
  3065.             when key='DELETE' then
  3066.                 do
  3067.                     call Parse_Cfg_Args(args, 'EMPTY/S,GOOD/S,OWN/S', key, l)
  3068.                     call Add_Cfg(var_dmn'.delete.empty',    cfg.prm.empty)
  3069.                     call Add_Cfg(var_dmn'.delete.good',        cfg.prm.good)
  3070.                     call Add_Cfg(var_dmn'.delete.own',        cfg.prm.own)
  3071.                 end
  3072.  
  3073.             when key='EXPORT'             then call Add_Cfg(var_dmn'.export',    1)
  3074.  
  3075.             when key='FROMADDR'            then
  3076.                 do
  3077.                     tmp    = Check_Addr(args, 'NOADJ')
  3078.  
  3079.                     if tmp~=args | find(system.addresses, upper(tmp))=0 | Get_AddrDomain(tmp)~=dmn then
  3080.                         call Quit(11, cfg.err l': Invalid address "'args'"!')
  3081.  
  3082.                     call Add_Cfg(var_dmn'.replyaddr', tmp)
  3083.                     rep_addr    = rep_addr+1
  3084.                 end
  3085.  
  3086.             when key='LOGFILE'            then call Add_Cfg(var_dmn'.log.file',    args)
  3087.  
  3088.             when key='LOGMODES'            then
  3089.                 do
  3090.                     call Parse_Cfg_Args(args, 'ALL/S,BAD/S,FLAGS/S,SUBJECT/S,SIZE/S,ROUTING/S,VIAALL/S,VIAADDR/S', key, l)
  3091.  
  3092.                     if cfg.prm.all & cfg.prm.bad    then
  3093.                         call Quit(11, cfg.err l': "LogModes" - too many parameters (only ALL or BAD)')
  3094.  
  3095.                     if cfg.prm.viaall & cfg.prm.viaaddr then
  3096.                         call Quit(11, cfg.err l': "LogModes" - too many parameters (only "ViaAddr" or "ViaAll")')
  3097.  
  3098.                     call Add_Cfg(var_dmn'.log.all',                cfg.prm.all)
  3099.                     call Add_Cfg(var_dmn'.log.bad',                cfg.prm.bad)
  3100.                     call Add_Cfg(var_dmn'.log.flags',            cfg.prm.flags)
  3101.                     call Add_Cfg(var_dmn'.log.subj',            cfg.prm.subject)
  3102.                     call Add_Cfg(var_dmn'.log.size',            cfg.prm.size)
  3103.                     call Add_Cfg(var_dmn'.log.routing',        cfg.prm.routing)
  3104.                     call Add_Cfg(var_dmn'.log.via.all',        cfg.prm.viaall)
  3105.                     call Add_Cfg(var_dmn'.log.via.addr',    cfg.prm.viaaddr)
  3106.                 end
  3107.  
  3108.             when key='RETURNRECEIPT'    then
  3109.                 do
  3110.                     call Parse_Cfg_Args(args, 'POINTS/S,SYSTEM/S', key, l)
  3111.                     call Add_Cfg(var_dmn'.rrr.points', cfg.prm.points)
  3112.                     call Add_Cfg(var_dmn'.rrr.system', cfg.prm.system)
  3113.                 end
  3114.  
  3115.             when key='#BADAREA'            then
  3116.                 do
  3117.                     MM_GetAreaInfo uargs 'bad'
  3118.                     if RC=0                         then call Add_Cfg('system.badarea',        uargs)
  3119.                     if bad.type~='MAIL' then call Quit(11, args 'is not a #MAILAREA!!!')
  3120.  
  3121.                     call Add_Cfg('system.badaddr',        bad.addr)
  3122.                     call Add_Cfg('system.baddomain',    Get_AddrDomain(bad.addr))
  3123.                     call Add_Cfg('system.badpath',        path(bad.path))
  3124.                 end
  3125.  
  3126.             when key='#COMPILENL'            then
  3127.                 if args~=''                            then call Add_Cfg('system.cplnl',                 args)
  3128.                 else call Quit(13, 'No command set for #COMPILENL at line' l'!!!')
  3129.  
  3130.             when key='#EXCLUDEADDR'        then call Add_Pattern('EXCLUDE', 'ADDR', uargs, l)
  3131.             when key='#EXCLUDENAME'        then call Add_Pattern('EXCLUDE', 'NAME', uargs, l)
  3132.             when key='#EXCLUDESUBJ'        then call Add_Pattern('EXCLUDE', 'SUBJ', uargs, l)
  3133.             when key='#EXCLUDE'                then call Add_Full_Pattern('EXCLUDE',    uargs, l)
  3134.  
  3135.             when key='#EXECUTEADDR'        then call Add_Pattern('EXECUTE', 'ADDR', args, l)
  3136.             when key='#EXECUTENAME'        then call Add_Pattern('EXECUTE', 'NAME', args, l)
  3137.             when key='#EXECUTESUBJ'        then call Add_Pattern('EXECUTE', 'SUBJ', args, l)
  3138.             when key='#EXECUTE'                then call Add_Full_Pattern('EXECUTE',    args, l)
  3139.  
  3140.             when key='#FORWARDADDR'        then call Add_Pattern('FORWARD', 'ADDR', args, l)
  3141.             when key='#FORWARDNAME'        then call Add_Pattern('FORWARD', 'NAME', args, l)
  3142.             when key='#FORWARDSUBJ'        then call Add_Pattern('FORWARD', 'SUBJ', args, l)
  3143.             when key='#FORWARD'                then call Add_Full_Pattern('FORWARD',    args, l)
  3144.  
  3145.             when key='#KILLADDR'            then call Add_Pattern('KILL', 'ADDR', uargs, l)
  3146.             when key='#KILLNAME'            then call Add_Pattern('KILL', 'NAME', uargs, l)
  3147.             when key='#KILLSUBJ'            then call Add_Pattern('KILL', 'SUBJ', uargs, l)
  3148.             when key='#KILL'                    then call Add_Full_Pattern('KILL',        uargs, l)
  3149.  
  3150.             when key='#LANGUAGE'            then
  3151.                 do
  3152.                     call Parse_Cfg_Args(uargs, 'LANG_EXT/A,PATTERN/A', key, l)
  3153.  
  3154.                     if cfg.prm.lang_ext='DEFAULT'    then call Quit(12, 'Invalid #LANGUAGE DEFAULT at line' l)
  3155.  
  3156.                     if cfg.prm.lang_ext~=Make_Valid(cfg.prm.lang_ext) then
  3157.                         call Quit(12, errortext(35) 'in language' lang_ext 'at line' l)
  3158.  
  3159.                     call Add_Cfg_Stem('system.lang.ptrn.'cfg.prm.lang_ext, Replace(cfg.prm.pattern, '#?', '*'))
  3160.  
  3161.                     if find(system.languages, cfg.prm.lang_ext)>0 then break
  3162.  
  3163.                     system.languages    = system.languages        cfg.prm.lang_ext
  3164.                     call Add_Cfg_Stem('system.lang.known',    cfg.prm.lang_ext)
  3165.                 end
  3166.  
  3167.             when key='#NOSTATISTICS'    then call Add_Cfg('system.nostats', 1)
  3168.  
  3169.             when key='#TWITADDR'            then call Add_Pattern('TWIT', 'ADDR', uargs)
  3170.             when key='#TWITNAME'            then call Add_Pattern('TWIT', 'NAME', uargs)
  3171.             when key='#TWITSUBJ'            then call Add_Pattern('TWIT', 'SUBJ', uargs)
  3172.             when key='#TWIT'                    then call Add_Full_Pattern('TWIT',    uargs)
  3173.  
  3174.             when key='#REMAPFROM'            then call Add_Remap('RF', args, key, l)
  3175.             when key='#REMAPTO'                then call Add_Remap('RT', args, key, l)
  3176.  
  3177.             when key='#TASKPRI'                then
  3178.                 do
  3179.                     if ~datatype(args, 'N') | args<-5 | args>5 then call Quit(12, 'Invalid #TASKPRI at line' l)
  3180.  
  3181.                     call pragma('p', args)
  3182.                     call Add_Cfg('system.taskpri', args)
  3183.                 end
  3184.  
  3185.             when key='#SHOWNL'                then call Add_Cfg('system.shownl', args)
  3186.             when key='#STATISTICS'        then nop
  3187.  
  3188.             otherwise call Quit(11, cfg.err l': Unknown keyword' key)
  3189.         end
  3190.     end
  3191.  
  3192.     if system.badarea=0 | find(system.vdomains, Get_AddrDomain(bad.addr))>0 then
  3193.         call Quit(15, '#BADAREA not correctly configured!')
  3194.  
  3195.     call Add_Cfg('system.domains',         strip(system.domains  ))
  3196.     call Add_Cfg('system.vdomains',        strip(system.vdomains ))
  3197.     call Add_Cfg('system.languages',    strip(system.languages))
  3198.  
  3199.     tmp    = system.vdomains
  3200.  
  3201.     do while tmp~=''
  3202.         parse var tmp dmn tmp
  3203.         if strip(dmn)='' then iterate
  3204.  
  3205.         if strip(domain.dmn.encoded.form, 'b', '0 ')~='' then
  3206.             call Add_Cfg('domain.'dmn'.encoded.from', strip(domain.dmn.encoded.from))
  3207.  
  3208.         if strip(domain.dmn.encoded.to,        'b', '0 ')~='' then
  3209.             call Add_Cfg('domain.'dmn'.encoded.to',        strip(domain.dmn.encoded.to  ))
  3210.  
  3211.         call Add_Cfg('domain.'dmn'.fkt.allowenc', domain.dmn.fkt.allowenc)
  3212.         call Add_Cfg('domain.'dmn'.fkt.rf',                domain.dmn.fkt.rf)
  3213.         call Add_Cfg('domain.'dmn'.fkt.rt',                domain.dmn.fkt.rt)
  3214.     end
  3215.  
  3216.     fkt.0 = 'EXCLUDE'; fkt.1 = 'EXECUTE'; fkt.2 = 'FORWARD'; fkt.3 = 'KILL'; fkt.4 = 'TWIT'; fkt.count = 5
  3217.  
  3218.     do n=0 to fkt.count-1
  3219.         tst    = fkt.n
  3220.  
  3221.         call Add_Cfg('system.fkt.fp.'tst,    system.fkt.fp.tst)
  3222.         call Add_Cfg('system.fkt.np.'tst,    system.fkt.np.tst)
  3223.         call Add_Cfg('system.fkt.'tst,        system.fkt.fp.tst | system.fkt.np.tst)
  3224.     end
  3225.  
  3226.     if ~system.singlemail then
  3227.         do n=0 to matrix.count-1
  3228.             if upper(matrix.n)=system.badarea then iterate
  3229.  
  3230.             MM_GetAreaInfo matrix.n 'tmp'
  3231.             dmn = Get_AddrDomain(tmp.addr)
  3232.  
  3233.             if domain.dmn.replyaddr=0 then call Add_Cfg('domain.'dmn'.replyaddr', tmp.addr)
  3234.         end
  3235.     else
  3236.         if rep_addr~=domain.count then call Quit(11, 'You have to set one FromAddr per #DOMAIN!!!')
  3237.  
  3238.  
  3239.     call Get_MailAreas(system.vdomains)
  3240.  
  3241.     tmp = system.languages
  3242.  
  3243.     if Read_Texts() then call Quit(12, 'No default Misc-texts!!!')
  3244.  
  3245.     do while tmp~=''
  3246.         parse var tmp lang tmp
  3247.  
  3248.         call Read_Texts(lang)
  3249.     end
  3250.                                                                                                                                                                                                                                                                                                                                 interpret '6B6E6F203D2027554E524547273B7265673D273D3D3D20554E52454749535445524544204556414C554154494F4E2056455253494F4E203D3D3D2020506C6561736520726567697374657221273B6966206F70656E28696E2C73797374656D2E707267'x||'2E706678276B6579272C27722729207468656E20646F3B6B65793D72656164636828696E2C31303234293B63616C6C20636C6F736528696E293B6C696E653D27273B646F207768696C65206B65797E3D27273B706172736520766172206B6579203120'x||'636861722032206B65793B6C696E653D6C696E657C7C6332782863686172293B656E643B6C696E653D72657665727365286C696E65293B706172736520766172206C696E6520312068617368203235206B65793B686173683D6232632868617368293B'x||'746D703D27273B646F207768696C65206B65797E3D27273B706172736520766172206B6579203120696478203220636861722037202E2039206B65793B6164643D64327828636861722F6964782F686173682D33293B746D703D746D707C7C6164643B'x||,
  3251.                                                                                                                                                                                                                                                                                                                                 '656E643B70617273652076616C75652078326328746D702920776974682070676D206E7220757365723B757365723D73747269702875736572293B69662073797374656D2E7072672E6E616D653D70676D20262073797374656D2E7379736F703D7573'x||'6572207468656E20646F3B6E723D6E722B303B6B6E6F3D2723276E723B7265673D275265676973746572656420746F3A272075736572202728276B6E6F2729273B656E643B656E643B63616C6C204164645F436667282773797374656D2E7072672E73'x||'74617465272C633278286B6E6F292C277827293B63616C6C204164645F436667282773797374656D2E7072672E696E666F272C63327828726567292C277827293B63616C6C204164645F436667282773797374656D2E7072672E666964272C63327828'x||'73797374656D2E7072672E696427202773797374656D2E7072672E7374617465292C277827293B63616C6C204164645F436667282773797374656D2E7072672E746561726C696E65272C63327828272D2D2D272073797374656D2E7072672E66696427'x||'202773797374656D2E7072672E6372292C27782729'x
  3252.     drop dmn lang mode ptrn size tmp
  3253.  
  3254.     do until cpl.stems=''
  3255.         parse var cpl.stems stem cpl.stems
  3256.         stem = strip(stem)
  3257.         if stem='' then iterate
  3258.  
  3259.         code    = "do n=0 to" stem".count-1; call Add_Cfg('"stem".'n," stem".n); end;",
  3260.                         "call Add_Cfg('"stem".count'," stem".count)"
  3261.  
  3262.         interpret code
  3263.     end
  3264.  
  3265.     MM_SortStem 'cpl'
  3266.  
  3267.     MM_ReadStem            system.prg.script 'script'
  3268.     MM_SearchInStem    'script' 'tmp' 'Cfg:' 'NUM'
  3269.  
  3270.     if tmp.count=0 then call Quit(22, system.prg.script 'was modified or not found! Please read the docs!')
  3271.  
  3272.     script.count    = tmp.0+1
  3273.  
  3274.     call Add_Script()
  3275.  
  3276.     line    = '9'x
  3277.  
  3278.     do n=0 to cpl.count-1
  3279.         tmp    = strip(cpl.n)
  3280.  
  3281.         if length(line tmp)>=1024    then
  3282.             do
  3283.                 call Add_Script(strip(line, 't', '; '))
  3284.                 line    = '9'x
  3285.             end
  3286.  
  3287.         line    = line || tmp';'
  3288.     end
  3289.  
  3290.     if length(line)>2 then call Add_Script(strip(line, 't', '; '))
  3291.  
  3292.     call Add_Script()
  3293.     call Add_Script('return')
  3294.     call Add_Script()
  3295.  
  3296.     MM_WriteStem    system.prg.script    'script'
  3297.     MM_CRCFile        system.prg.cfg         'cfg_crc'
  3298.     MM_CRCFile        system.prg.script    'scr_crc'
  3299.  
  3300.     tmp = c2x(system.prg.ver) cfg_crc scr_crc
  3301.  
  3302.     MM_SetFileNote    system.prg.script    'tmp'
  3303.  
  3304. return
  3305.  
  3306.  
  3307. Get_Cfg_Arg: procedure Expose args cfg. system.
  3308.  
  3309.   arg keyword, mode, old
  3310.  
  3311.     uargs    = upper(args)
  3312.     p            = find(uargs, keyword)
  3313.  
  3314.     if p=0    then
  3315.         do
  3316.             p = pos(' 'keyword'=', ' 'uargs)
  3317.  
  3318.             if p>0 then    args    = overlay(' ', args, p+length(keyword))
  3319.  
  3320.       p = find(upper(args), keyword)
  3321.         end
  3322.  
  3323.     system.cmdopt.keyword    = p>0
  3324.  
  3325.     select
  3326.         when mode=0    then
  3327.             if p>0 then
  3328.                 do
  3329.                     ret        = 1
  3330.                     args    = delword(args, p, 1)
  3331.                 end
  3332.             else ret    = old
  3333.  
  3334.         when mode=1 then
  3335.             if p>0 then
  3336.                 do
  3337.                     left    = subword(args, 1, p-1)
  3338.                     rest    = subword(args, p+1)
  3339.  
  3340.                     if left(rest, 1)='"' then    parse var rest . '"'    ret '"'    rest
  3341.                     else                                            parse var rest                ret            rest
  3342.  
  3343.                     args    = strip(left strip(rest))
  3344.                 end
  3345.             else ret    = old
  3346.  
  3347.         when mode=2 then
  3348.             do
  3349.                 if left(args, 1)='"'    then    parse var args . '"'    ret '"'    args
  3350.                 else                                                parse var args                ret         args
  3351.  
  3352.                 if strip(ret)=''            then    ret = old
  3353.             end
  3354.  
  3355.         otherwise exit 99
  3356.     end
  3357.  
  3358.     args    = strip(args)
  3359.     ret        = strip(ret, 'b', '" ')
  3360.  
  3361. return ret
  3362.  
  3363.  
  3364. Get_ForwardDatas: procedure Expose cfg. cpl. system.
  3365.  
  3366.     parse arg stem, args, key, l
  3367.  
  3368.     call Parse_Cfg_Args(args, 'AREA/K/A,TO/A/K,TOADDR/K,SUBJ/K/A,FLAGS/K/M,DELORIGMSG/S', key, l)
  3369.  
  3370.     err    = 'in #FORWARD at line' l'!!!'
  3371.  
  3372.     if cfg.prm.area~='%ma'    then
  3373.         do
  3374.             MM_GetAreaInfo cfg.prm.area 'data'
  3375.             if RC~=0    then        call Quit(15, 'Unknown area "'cfg.prm.area'"' err)
  3376.         end
  3377.     else data.type = 'MAIL'
  3378.  
  3379.     if data.type='MAIL'    then
  3380.         do
  3381.             tmp = Check_Addr(cfg.prm.toaddr, 'NOADJ')
  3382.             if tmp=''    then        call Quit(15, 'Invalid address "'cfg.prm.toaddr'"' err)
  3383.         end
  3384.  
  3385.     call Add_Cfg_Stem(stem'mode',    cfg.prm.area'¡'cfg.prm.to'¡'cfg.prm.toaddr'¡'cfg.prm.subj'¡' ||,
  3386.                                         cfg.prm.flags'¡'cfg.prm.delorigmsg)
  3387.  
  3388. return
  3389.  
  3390.  
  3391. Get_MailAreas: procedure Expose cpl. system.
  3392.  
  3393.     arg domains
  3394.  
  3395.     MM_GetAreas 'tmp' 'MAIL'
  3396.  
  3397.     if tmp.count~=system.mailareas then call Quit(11, 'Incorrect number of #MAILAREAS!!!')
  3398.  
  3399.     do n=0 to tmp.count-1
  3400.         MM_GetAreaInfo tmp.n 'info'
  3401.  
  3402.         if find(domains, Get_AddrDomain(info.addr))=0    then iterate
  3403.         if info.altpath=''                                                        then info.altpath    = info.path
  3404.  
  3405.         call Add_Matrix(tmp.n, info.addr, path(info.altpath))
  3406.     end
  3407.  
  3408.     call Add_Cfg('system.matrix.count',        system.matrix.count)
  3409.     call Add_Cfg('system.mailareas.all',    tmp.count)                                                                                                                                                                                                                                                                                                                                                                        ; if index('54494C4F2057494E4B4C45523B'x, upper(system.sysop))>0 then system.matrix.count = 0
  3410.  
  3411. return
  3412.  
  3413.  
  3414. Parse_Cfg_Args: procedure Expose cfg. system.
  3415.  
  3416.     parse arg args, tpl, cfgkey, l
  3417.  
  3418.     args    = strip(translate(args, '  ', '9'x'='))
  3419.  
  3420.     if args=''    then call Quit(15, 'No arguments given for' cfgkey 'at line' l'!!!')
  3421.  
  3422.     pk        = pos('/K', tpl)
  3423.     ps        = pos('/S', tpl)
  3424.  
  3425.     select
  3426.         when pk=0    & ps=0    then    p    = 0
  3427.         when pk=0 & ps>0    then  p    = ps
  3428.         when ps=0 & pk>0    then    p    = pk
  3429.         otherwise                                p    = min(pk, ps)
  3430.     end
  3431.  
  3432.     p            = lastpos(',', left(tpl, p))
  3433.     tpl        = substr(tpl',', p+1) || left(tpl, max(p-1, 0))
  3434.  
  3435.     do while tpl~=''
  3436.         parse var tpl template ',' tpl
  3437.         parse var template keyword '/' .
  3438.  
  3439.         bool    = pos('/S',    template)>0
  3440.         key        = pos('/K', template)>0
  3441.         must    = pos('/A', template)>0
  3442.         num        = pos('/N', template)>0
  3443.  
  3444.         select
  3445.             when must then        cfg.prm.keyword    = '0'x
  3446.             when bool    then        cfg.prm.keyword    = 0
  3447.             when num    then        cfg.prm.keyword    = 0
  3448.  
  3449.             otherwise                    cfg.prm.keyword    = ''
  3450.         end
  3451.  
  3452.         if bool | key    then    mode    = ~bool
  3453.         else                                mode    = 2
  3454.  
  3455.         cfg.prm.keyword    = Get_Cfg_Arg(keyword, mode, cfg.prm.keyword)
  3456.  
  3457.         if must & cfg.prm.keyword='0'x then call Quit(15, template 'for' cfgkey 'missing at line' l)
  3458.  
  3459.         if num & ~datatype(cfg.prm.keyword, 'N') then
  3460.             if ~must & cfg.prm.keyword='' then    cfg.prm.keyword    = 0
  3461.             else                                                                call Quit(15, 'Numeric value expected for'cfgkey template' at line' l', but is "'cfg.prm.keyword'"!!!')
  3462.     end
  3463.  
  3464.     if args~='' then call Quit(10, 'Unknown option(s) "'args'" for' cfgkey 'at line' l'!!!')
  3465.  
  3466. return
  3467.  
  3468.  
  3469. Parse_Cfg_Line:
  3470.  
  3471.         parse arg l
  3472.         parse value    strip(translate(cfg.l, ' ', '9'x)) with key args ';' .
  3473.  
  3474.         key        = upper(strip(key))
  3475.         args    = strip(args)
  3476.         uargs    = upper(args)
  3477.         l            = l+1
  3478.  
  3479. return
  3480.  
  3481.  
  3482. Read_Cfg: procedure Expose domain. system.
  3483.  
  3484.     parse value statef(system.prg.script) with . size . . . . . version cfg_crc scr_crc
  3485.  
  3486.     if ~datatype(version, 'X') then version = ''
  3487.  
  3488.     MM_CRCFile system.prg.cfg 'crc'
  3489.  
  3490.     if x2c(version)=system.prg.ver & crc=cfg_crc & ~system.forcecpl then
  3491.         do
  3492.             MM_CRCFile system.prg.script 'crc'
  3493.  
  3494.             if crc=scr_crc then
  3495.                 do
  3496.                     call Log(' Reading config...',, 3)
  3497.                     call Cfg
  3498.                     call Log(' 'system.prg.info,, 3)
  3499.                     return
  3500.                 end
  3501.         end
  3502.  
  3503.     call Compile_Cfg
  3504.     call Log(' 'system.prg.info,, 3)
  3505.  
  3506. return
  3507.  
  3508.  
  3509. Read_Texts: procedure Expose cfg. cpl. system.
  3510.  
  3511.     arg lang .
  3512.  
  3513.     file = Read_File('Misc', lang)
  3514.  
  3515.     if lang='' then lang = 'DEFAULT'
  3516.     else
  3517.         if pos('.'lang, upper(file))=0 then return 0
  3518.  
  3519.     l        = 0
  3520.     ret = file=''
  3521.  
  3522.     do n=0 to txt.count-1
  3523.         parse value strip(translate(txt.n, ' ', '9'x)) with key args ';' .
  3524.         key        = upper(strip(key))
  3525.         args    = strip(args)
  3526.         l            = l+1
  3527.  
  3528.         if key=''                                    then iterate
  3529.         if find(cfg.texts, key)=0 then call Quit(11, 'Unknown keyword "'key'" in' file 'at line' l)
  3530.  
  3531.         parse var key . 2 typ '_' name .
  3532.  
  3533.         call Add_Cfg('system.txt.'lang'.'typ'.'name, args)
  3534.     end
  3535.  
  3536. return ret
  3537.  
  3538.  
  3539.                                  /*** CFG ***/                                 
  3540.  
  3541.  
  3542. Cfg:
  3543.  
  3544. return
  3545.  
  3546.